package com.iot08.api.service.supplement;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.maps.model.CellTower;
import com.google.maps.model.GeolocationResult;
import com.google.maps.model.WifiAccessPoint;
import com.iot08.api.config.properties.ListDataProperties;
import com.iot08.api.config.redis.*;
import com.iot08.api.constant.Constant;
import com.iot08.api.service.*;
import com.iot08.api.utils.*;
import com.iot08.api.utils.buckettool.BaseBucketTool;
import com.iot08.common.config.mongodb.query.UpdateVO;
import com.iot08.common.constant.*;
import com.iot08.common.entity.app.*;
import com.iot08.common.utils.*;
import com.mongodb.client.FindIterable;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.iot08.api.config.properties.GdBdConfig;
import org.springframework.util.CollectionUtils;

import static sun.audio.AudioDevice.device;

/**
 * @version 1.0
 * @Description:
 * @time: 2021/1/20 16:47
 * @author: Rock
 */
@Slf4j
@Service
public class TCPService {

    @Resource
    private DeviceService deviceService;
    @Resource
    private DeviceDataFlowService deviceDataFlowService;
    @Resource
    private DeviceMessageService deviceMessageService;
    @Resource
    private DeviceCostFlowService deviceCostFlowService;
    @Resource
    private DeviceModelsService deviceModelsService;
    @Resource
    private ListDataProperties listDataProperties;
    @Resource
    private KafkaService kafkaService;

    @Resource
    private DeviceVideoService deviceVideoService;
    @Resource
    private OpenInterfaceService openInterfaceService;

    @Resource
    private RedisFilterService redisFilterService;

    @Resource
    private SystemLimitService systemLimitService;

    //用户用户存储 token 的redis  (现在用的还是主集群的,后面要拆分掉)
    //RedisAccountClusterService

    //储存imei信息的 主要redis 集群
    //RedisClusterService

    @Resource
    private VipMemberService vipMemberService;

    //碰碰交友
    @Resource
    private RedisPPMFAndWeatherService redisPPMFAndWeatherService;

    @Resource
    private RedisClusterService redisClusterService;
    //视频
    @Resource
    private RedisVideoService redisVideoService;

    @Resource
    private PpChatGroupService ppChatGroupService;

    @Resource
    private ChatGroupService chatGroupService;

    @Resource
    private SingleChatGroupService singleChatGroupService;

    @Resource
    private DevicePhotographService devicePhotographService;
    @Resource
    private PpChatGroupMessageService ppChatGroupMessageService;
    @Resource
    private DeviceImsiService deviceImsiService;
    @Resource
    private ChatGroupMessageService chatGroupMessageService;
    @Resource
    private SingleChatGroupMessageService singleChatGroupMessageService;
    @Resource
    private AccountService accountService;
    @Resource
    private WeixinService weixinService;
    @Resource
    private DeviceStepDailyService deviceStepDailyService;
    @Resource
    private IotCardInfoService iotCardInfoService;
    @Resource
    private FilterWifiVendorModelService filterWifiVendorModelService;
    @Resource
    private MacToCompanyService macToCompanyService;
    @Resource
    private DeviceLocationService deviceLocationService;
    @Resource
    private DeviceLocationDailyService deviceLocationDailyService;
    @Resource
    private HeartRateRecordService heartRateRecordService;
    @Resource
    private HeartRateSettingService heartRateSettingService;
    @Resource
    private BloodPressureRecordService bloodPressureRecordService;
    @Resource
    private BloodPressureSettingService bloodPressureSettingService;
    @Resource
    private BloodOxygenRecordService bloodOxygenRecordService;
    @Resource
    private BloodOxygenSettingService bloodOxygenSettingService;
    @Resource
    private DeviceLastMissedVideoCallService deviceLastMissedVideoCallService;
    @Resource
    private AnimalHeatRecordService animalHeatRecordService;
    @Resource
    private AnimalHeatSettingService animalHeatSettingService;

    @Resource
    private DeviceCmsgService deviceCmsgService;

    @Resource
    private DeviceAppManagerService deviceAppManagerService;
    @Resource
    private DrinkDataService drinkDataService;

    @Resource
    private DeviceSanWordService deviceSanWordService;

    @Resource
    private DeviceAppRecordService deviceAppRecordService;

    @Resource
    private ContentSecurityUtil contentSecurityUtil;

    @Resource
    private DevHCYAuthService devHCYAuthService;

    @Resource
    private HCYAuthLogService hcyAuthLogService;

    @Resource
    private HCYPhoneCloudService hcyPhoneCloudService;

    @Resource
    private GoogleMapsApi googleMapsApi;

    @Resource
    private BaseBucketTool baseBucketTool;

    @Resource
    private SetWifiService setWifiService;

    @Resource
    private CallRecordService callRecordService;

    @Resource
    private GdBdConfig gdBdConfig;

    @Resource
    private UserSmsNotifyService userSmsNotifyService;

    @Resource
    private RollTimeDataService rollTimeDataService;

    @Resource
    private SmsService smsService;

    @Resource
    private DeviceLocationOriginalRecordService deviceLocationOriginalRecordService;

    @Resource
    private DeviceBdLocationDataService deviceBdLocationDataService;

    @Resource
    private WechatContactService wechatContactService;

    @Resource
    private DeviceSingleChatGroupService deviceSingleChatGroupService;

    @Resource
    private DeviceSingleChatGroupMessageService deviceSingleChatGroupMessageService;

    @Resource
    private SensitivityFileGroupMessageService sensitivityFileGroupMessageService;

    @Resource
    private SensitivityFileSingleMessageService sensitivityFileSingleMessageService;

    @Resource
    private RedisAccountService redisAccountService;

    @Value("${weather.HeFengWeatherKey:}")
    private String HeFengWeatherKey;

    @Value("${wayz.key}")
    private String wayzKey;
    @Value("${domain.name}")
    private String domainName;
    @Value("${isUserJAVAPositionCorrection}")
    private String isUserJAVAPositionCorrection;

    private static final String INFO_CODE = "10000";

    @Value("${TXZLProduct_id}")
    private String TXZLProduct_id;
    @Value("${TXZLwxgzMasterId}")
    private String TXZLwxgzMasterId;

    // 注入环境标识
    @Value("${spring.profiles.active}")
    private String activeProfile;

    // 使用的 维智定位的 比例 0~1
    public Double useWeiZhiLocateBL;


    /**
     * @param imei
     * @param message
     * @time: 2021/1/28 17:59
     * @author: Rock
     * @version 1.0
     */
//    @Async
    public void cmdTempFunc(String imei, JSONObject message) {
        if (StringUtil.isNotBlank(message.get(ConstKafka.TYPE))) {
            Device device = deviceService.findById(imei);
            String kafkaDataType = String.valueOf(message.get(ConstKafka.TYPE));
            log.info("进入了解析方法，kafka的数据类型为：{}", kafkaDataType);
            if (ObjectUtils.isEmpty(device)) {
                log.error("设备不存在. imei={}.", imei);
                return;
            } else if (CollectionUtils.isEmpty(device.getUserMap()) && !"loginTime".equals(kafkaDataType)
                    && !"upWeather".equals(kafkaDataType) && !"appAndFnReport".equals(kafkaDataType)) {
                log.error("设备未绑定用户. imei={}.", imei);
                return;
            }
            switch (kafkaDataType) {
                case "1":
                    type_1(device, message);
                    break;
                case "loginTime":
                    type_loginTime(device, message);
                    break;
                case "ICCID": {
                    message.put("imsi", message.getString("ICCID"));
                    type_loginTime(device, message);
                }
                break;
                case "offlineTime": {
                    //离线上传的指令
                }
                break;
                //获取房间数
                case "upGetRoomCount":
                    type_upGetRoomCount(device, message);
                    break;
                //上传位置 里面包含 低电报警 和 SOS 报警
                case "location":
                    type_7(device, message);
                    break;
                case "ppFriends":
                    type_ppFriend(device, message);
                    break;
                case "upPpFriendVoice":
                    type_ppSingleGroupMsg(device, message);
                    break;
                case "upPpFriendDel":
                    type_ppFriendDel(device, message);
                    break;
                case "upPpFriendsGet":
                    type_ppFriendGet(device, message);
                    break;
                //请求经纬度 (暂无)
                case "coordinate":
//                    type_7(device, message);
                    break;
                case "deviceReset":
                    type_deviceReset(device, message);
                    break;
                case "11":
                    type_11(device, message);
                    break;
                case "upWeather":
                    type_upWeather(device, message);
                    break;
                case "19":
                    type_19(device, message);
                    break;
                //远程拍照
                case "upRemoteImage":
                    type_upRemoteImage(device, message);
                    break;
               /* case "battery":
                    type_battery(device, message);
                    break;
                case "step":
                    type_step(device, message);
                    break;
                case "rollNum":
                    type_rollNum(device, message);
                    break;*/
                case "batteryStepRollNum":
                    type_battery_step_rollNum(device, message);
                    break;
                //微聊语音上传
                case "upChatVoice":
                    //微聊图片
                case "chatImage":
                    //碰碰交友的语音和图片
                case "upPpVoice":
                case "upPpImage":
                    type_groupmsg(device, message);
                    break;
                case "upAppContactGet":
                    type_AppContactGet(device, message);
                    break;
                //新版微聊协议的设备上报的数据
                case "upNewChatVoice":
                case "upNewChatImage":
                case "upNewChatEmoji":
                    type_newChatmsg(device, message);
                    break;
                case "groupmsg":
                    type_newGroupmsg(device, message);
                    break;
                //话费流量上传
                case "inquire":
                    type_uploadCostFlow(device, message);
                    break;
                //获取手表可视频的用户反馈给手表
                case "upGetVideoUserList":
                    type_upGetVideoUserList(device, message);
                    break;
                case "upVideoCallWithAPP":
                    type_upVideoCallWithAPP(device, message);
                    break;
                case "upWatchHangUp":
                    type_upWatchHangUp(device, message);
                    break;
                case "upVideoCallTime":
                    type_upVideoCallTime(device, message);
                    break;
                case "63":
                    type_63(device, message);
                    break;
                case "upMakeFriend":
                    type_upMakeFriend(device, message);
                    break;
                case "upSyncFriend":
                    type_upSyncFriend(device, message);
                    break;
                case "upDelFriend":
                    type_upDelFriend(device, message);
                    break;
                case "77":
                    type_77(device, message);
                    break;
                case "81":
                    type_81(device, message);
                    break;
                case "95":
                    type_95(device, message);
                    break;
                case "upHeartRate":
                case "upBodyTemperature":
                case "upBP":
                case "upBO":
                case "upBS":
                    type_101(device, message);
                    break;
                case "107":
                case "upICCID": //上传ICCID
                    type_107(device, message);
                    break;
                case "upSMS": //代收短信
                    type_upSMS(device, message);
                    break;
                case "wifiSearch": // WIFI搜索功能
                    type_wifiSearch(device, message);
                    break;
                case "wifiSet":
                    type_wifiSet(device, message);
                    break;
                case "wifiCur":
                    type_wifiCur(device, message);
                    break;
                case "upScanWord": //手表 上报扫描词
                    type_upScanWord(device, message);
                    break;
                case "upAppRecord": //设备 上报应用记录
                    type_upAppRecord(device, message);
                    break;
                //移动和彩云云盘
                case "151":
                    type_151(device, message);
                    break;
                case "153":
                    type_153(device, message);
                    break;
                case "155":
                    type_155(device, message);
                    break;
                case "157":
                    type_157(device, message, true);
                    break;
                //App应用监管-应用上报
                case "appAndFnReport":
                    type_appFn(device, message);
                    break;
                case "drinkDataReport":
                    this.type_drinkData(device, message);
                    break;
                case "callLog":
                    this.type_callLog(device, message);
                    break;
                case "payInPwdVCode":
                    this.payInPwdVCode(device, message);
                    break;
                case "payInPwdVCodeCheck":
                    this.payInPwdVCodeCheck(device, message);
                    break;
                default:
                    log.error("type is not found. imei={}, message={}", imei, message);
                    break;
            }
        }
    }

    /**
     * 支付密码找回验证码校验处理
     *
     * @param device  设备对象
     * @param message 消息内容
     */
    private void payInPwdVCodeCheck(Device device, JSONObject message) {
        try {
            String imei = device.getId();
            String vcode = message.getString("vcode");

            log.info("支付密码找回验证码校验. imei={}, vcode={}", imei, vcode);

            if (StringUtil.isBlank(vcode)) {
                log.error("验证码为空. imei={}", imei);
                sendPayInPwdVCodeCheckResponse(imei, "0", null);
                return;
            }
            // 1. 检查当天验证次数
            String today = new SimpleDateFormat("yyyyMMdd").format(new Date());
            String verifyKey = "payInPwdVCodeVerify:" + imei + ":" + today;
            String verifyStr = redisVideoService.get(verifyKey);
            int verifyCount = verifyStr != null ? Integer.parseInt(verifyStr) : 0;

            // 更新验证次数
            verifyCount++;
            // 每天最多验证5次
            int maxVerifyCount = 5;
            int remaining = maxVerifyCount - verifyCount;
            if (remaining < 0) {
                log.warn("今日验证次数已用完. imei={}", imei);
                sendPayInPwdVCodeCheckResponse(imei, "2", "0");
                return;
            }
            redisVideoService.setex(verifyKey, 86400, String.valueOf(verifyCount));
            // 2. 获取存储的验证码
            String storedCode = redisVideoService.get(CacheEnum.payInPwdVCode.getKey() + imei);

            if (storedCode == null) {
                log.error("验证码不存在或已过期. imei={}", imei);
                sendPayInPwdVCodeCheckResponse(imei, "3", String.valueOf(remaining));
                return;
            }


            // 4. 验证验证码
            if (vcode.equals(storedCode)) {
                // 验证成功
                log.info("验证码验证成功. imei={}", imei);

                // 清除验证码
                redisVideoService.del(CacheEnum.payInPwdVCode.getKey() + imei);


                sendPayInPwdVCodeCheckResponse(imei, "1", null);
            } else {
                // 验证失败
                log.warn("验证码错误. imei={}, remaining={}", imei, remaining);
                sendPayInPwdVCodeCheckResponse(imei, "3", String.valueOf(remaining));
            }

        } catch (Exception e) {
            log.error("处理支付密码找回验证失败. imei={}", device.getId(), e);
            sendPayInPwdVCodeCheckResponse(device.getId(), "0", null);
        }
    }

    /**
     * 发送支付密码找回验证码校验响应给设备
     */
    private void sendPayInPwdVCodeCheckResponse(String imei, String status, String num) {
        JSONObject result = new JSONObject();
        result.put("type", "payInPwdVCodeCheck");
        result.put("imei", imei);
        result.put("status", status);
        result.put("num", num);


        kafkaService.emitDeviceDemand(imei, "payInPwdVCodeCheck", result, imei);
    }


    /**
     * 支付密码找回上报协议处理
     * 设备请求发送验证码
     *
     * @param device  设备对象
     * @param message 消息内容
     */
    private void payInPwdVCode(Device device, JSONObject message) {
        try {
            String imei = device.getId();
            log.info("支付密码找回请求. imei={}", imei);

            // 1. 查找管理员
            String adminUserId = null;
            if (device.getUserMap() != null) {
                Map<String, Map<String, Object>> userMap = device.getUserMap();
                for (String key : userMap.keySet()) {
                    JSONObject item = new JSONObject(userMap.get(key));
                    if (item.containsKey("identity") && "admin".equals(item.getString("identity"))) {
                        adminUserId = key;
                        break;
                    }
                }
            }


            // 2. 检查今日请求次数（验证码生成次数）
            String today = new SimpleDateFormat("yyyyMMdd").format(new Date());
            String countKey = "payInPwdVCodeCount:" + imei + ":" + today;
            String countStr = redisVideoService.get(countKey);
            int count = countStr != null ? Integer.parseInt(countStr) : 0;
//
//            if (count >= 5) {
//                log.warn("今日验证码生成次数已达上限. imei={}, count={}", imei, count);
//                sendPayInPwdVCodeResponse(imei, "2"); // 尝试次数太多
//                return;
//            }

            // 3. 生成4位验证码
            String code = String.valueOf((int) ((Math.random() * 9 + 1) * 1000));

            // 4. 存储验证码到Redis（单独的键，新的会替换旧的）
            redisVideoService.setex(CacheEnum.payInPwdVCode.getKey() + imei, CacheEnum.payInPwdVCode.getExpire(), code);

            // 5. 推送验证码给管理员（通过APP推送）
            if (adminUserId != null) {
                JSONObject pushData = new JSONObject();
                pushData.put("type", "payInPwdVCode");
                pushData.put("imei", imei);
                pushData.put("deviceName", device.getName());
                pushData.put("vcode", code);

                // 推送给管理员APP
                kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_3, adminUserId, pushData);


                count++;
                redisVideoService.setex(countKey, 86400, String.valueOf(count)); // 存储新的计数值


                sendPayInPwdVCodeResponse(imei, "1"); // 成功
            } else {
                log.error("支付密码找回验证码推送失败. imei={}", imei);
                sendPayInPwdVCodeResponse(imei, "0"); // 异常
            }

        } catch (Exception e) {
            log.error("推送验证码失败. imei={}", device.getId(), e);
            sendPayInPwdVCodeResponse(device.getId(), "0"); // 异常
        }


    }

    private void sendPayInPwdVCodeResponse(String imei, String status) {
        JSONObject result = new JSONObject();
        result.put("type", "payInPwdVCode");
        result.put("imei", imei);
        result.put("status", status);
        kafkaService.emitDeviceDemand(imei, "payInPwdVCode", result, imei);
    }


    /**
     * 处理获取群聊房间剩余人员数
     *
     * @param device  设备对象
     * @param message 消息内容
     */
    private void type_upGetRoomCount(Device device, JSONObject message) {
        try {
            // 获取房间号 (vid格式: IMEI_时间戳)
            String vid = message.getString("video_id");
            if (StringUtil.isBlank(vid)) {
                log.error("获取群聊房间人数失败，房间号为空. imei={}", device.getId());
                return;
            }

            // 查询房间人数
            int count = getGroupVideoRoomCount(vid);


            // 构造回复数据
            JSONObject result = new JSONObject();
            result.put("type", "getRoomCount");
            result.put("video_id", vid);
            result.put("count", count);

            // 推送给设备
            kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_GETRYROOMCOUNT, result, device.getId());

            log.info("获取群聊房间人数成功. imei={}, roomNum={}, count={}",
                    device.getId(), vid, count);

        } catch (Exception e) {
            log.error("处理获取群聊房间人数失败. imei={}, message={}",
                    device.getId(), message, e);
        }


    }

    /**
     * 获取群视频房间的实际人数
     *
     * @param vid 视频ID（房间号）
     * @return 房间内人数
     */
    private int getGroupVideoRoomCount(String vid) {
        try {
            // 1. 通过遍历Redis找到存储这个vid的群组ID
            String groupId = null;
            Set<String> keys = redisVideoService.keys(CacheEnum.GROUP_VIDEO.getKey() + "*");

            for (String key : keys) {
                String value = redisVideoService.get(key);
                if (vid.equals(value)) {
                    // 找到了存储这个vid的key
                    groupId = key.replace(CacheEnum.GROUP_VIDEO.getKey(), "");
                }
            }

            if (StringUtil.isBlank(groupId)) {
                log.info("未找到使用该房间号的群组. vid={}", vid);
                return 0;
            }

            // 2. 获取群组信息
            ChatGroup chatGroup = chatGroupService.findById(groupId);
            if (chatGroup == null) {
                log.error("群组不存在. groupId={}", groupId);
                return 0;
            }

            // 3. 统计在线人数
            int count = 0;

            // 检查设备成员
            if (CollectionUtil.isNotEmpty(chatGroup.getDeviceMap())) {
                for (String deviceImei : chatGroup.getDeviceMap().keySet()) {
                    String deviceStatus = redisVideoService.get(CacheEnum.GROUP_VIDEO.getKey() + deviceImei);
                    if (vid.equals(deviceStatus)) {
                        count++;
                    }
                }
            }

            // 检查APP用户成员
            if (CollectionUtil.isNotEmpty(chatGroup.getMemberMap())) {
                for (String memberOpenId : chatGroup.getMemberMap().keySet()) {
                    String memberStatus = redisVideoService.get(CacheEnum.GROUP_VIDEO.getKey() + memberOpenId);
                    if (vid.equals(memberStatus)) {
                        count++;
                    }
                }
            }

            return count;

        } catch (Exception e) {
            log.error("获取群视频房间人数失败. vid={}", vid, e);
            return 0;
        }
    }

    /**
     * 更新设备信息
     *
     * @param device
     * @param message
     * @time: 2021/1/20 17:34
     * @author: Rock
     * @version 1.0
     */
    private void type_1(Device device, JSONObject message) {
        Update updateDevice = new Update();
        if (message.containsKey(ConstKafka.TOKEN)) {
            updateDevice.set("token", message.get(ConstKafka.TOKEN));
        }
        if (message.containsKey(ConstKafka.TTIME)) {
            updateDevice.set("ttime", message.get(ConstKafka.TTIME));
        }
        if (message.containsKey("autoModules") && Const.INT_1.equals(device.getIsWatchCustomFunction())) {
            updateDevice.set("watchCustomFunction", message.get("autoModules"));
        }
        if (message.containsKey("platform")) {
            updateDevice.set("platform", message.get("platform"));
        }
        deviceService.updateById(device.getId(), updateDevice);
    }

    /**
     * @param [device, message]
     * @return void
     * @description 碰碰交好友，建立好友连接的数据解析
     * 缓存wifi信息+当前时间戳信息，查找到300s内，有两个相同的wifi的mac地址即可认为两个设备在进行碰碰交友
     * 解析数据，比较两台设备的距离，没有超过300米且在300s，则认为两设备再进行碰碰交友
     * 没有找到交友对象，就将数据粗如缓存中，保存时间5分钟
     * @author cwj
     * @date @time 2025/7/2 16:45
     **/
    private void type_ppFriend(Device device, JSONObject message) {
        //检查设备时间戳
        if (!StringUtil.isNumeric(message.get("location_time"))) {
            log.error("碰碰交好友，定位点缺少时间戳 imei={} ", device.getId());
            return;
        }
        ObjectMapper objectMapper = new ObjectMapper();
        Long currentTimestamp = System.currentTimeMillis() / 1000;
        String lockKey = "lock:" + Constant.PP_MAKE_FRIEND_KEY;
        String lockValue = UUID.randomUUID().toString();
        double lon = 0;
        double lat = 0;
        if (message.get("battery") != null) {
            type_battery(device, message);
        }
        //解析步数
        if (message.get("stepNumber") != null) {
            message.put("number", message.get("stepNumber"));
            type_step(device, message);
        }
        //解析翻滚次数
        if (message.get("rollNumber") != null) {
            message.put("number", message.get("rollNumber"));
            type_rollNum(device, message);
        }
        if (message.containsKey("gps")) {
            //进行一下坐标转换
            JSONObject addGPSDict = message.getJSONObject("gps");
            double[] changeGPS = CoordinateConvert.wgs2GCJ(addGPSDict.getDouble("lat"), addGPSDict.getDouble("lon"));
            addGPSDict.put("lat", changeGPS[0]);
            addGPSDict.put("lon", changeGPS[1]);
            lat = changeGPS[0];
            lon = changeGPS[1];
            //成功获得缓存锁才执行
            if (!lockedRedis(lockKey, lockValue)) {
                log.error("多次尝试后仍未获取Redis锁，放弃执行");
                sendPpMakeFriendStatus(device.getId(), new JSONObject(), 0);
                return;
            }
            try {
                //获取碰碰交友上报的缓存数据，去掉缓存中超过5分钟的数据
                Map<String, Object> ppMap = getRedisPpFriendsData(objectMapper, device.getId());
                if (MapUtils.isEmpty(ppMap)) {
                    //将数据存入缓存中
                    ppMap = new HashMap<>();
                    //设置缓存数据
                    settingPpMapCache(device.getId(), currentTimestamp, lon, lat, ppMap, null, objectMapper);
                } else {
                    boolean isFindFriend = false;
                    //根据经纬度进行碰碰交友
                    isFindFriend = ppMakeFrendByLonLat(device, ppMap, lon, lat, isFindFriend, currentTimestamp);
                    if (!isFindFriend) {
                        //设置缓存数据
                        settingPpMapCache(device.getId(), currentTimestamp, lon, lat, ppMap, null, objectMapper);
                    }
                }
            } catch (JsonProcessingException je) {
                log.error("获取碰碰交好友的缓存数据失败，请检查原因");
            } finally {
                redisPPMFAndWeatherService.unlock(lockKey, lockValue);
            }
        } else {
            //基站信息解析
            JSONArray wifis = message.getJSONArray("wifi");
            JSONArray cells = message.getJSONArray("baseStation");
            Integer cdma = message.getInteger("baseStationType");
            Integer mcc = message.getInteger("mcc");
            //判断是否符合定位规则的数据
            boolean parsing = checkIsParsing(cells, cdma, wifis);
            if (!parsing) {
                //无效数据，无法处理进行碰碰交好友
                log.error("imei:{} 不符合定位规则数据： message={}", device.getId(), message);
                //碰碰交友失败
                sendPpMakeFriendStatus(device.getId(), new JSONObject(), 0);
                return;
            }
            if (mcc == null || mcc == 460) {
                //成功获得缓存锁才执行
                if (!lockedRedis(lockKey, lockValue)) {
                    log.error("多次尝试后仍未获取Redis锁，放弃执行");
                    //碰碰交友失败
                    sendPpMakeFriendStatus(device.getId(), new JSONObject(), 0);
                    return;
                }
                try {
                    boolean isFindFriend = false;
                    //获取碰碰交友上报的缓存数据，去掉缓存中超过5分钟的数据
                    Map<String, Object> ppMap = getRedisPpFriendsData(objectMapper, device.getId());
                    if (MapUtils.isEmpty(ppMap)) {
                        //将数据存入缓存中
                        ppMap = new HashMap<>();
                        String lonLatResult = bdAnalysisLocation(device.getId(), wifis, cells, cdma.toString(), message.getInteger("kafkaModelType"));
                        if (StringUtils.isNotEmpty(lonLatResult)) {
                            String[] lonlats = lonLatResult.split(",");
                            lon = Double.parseDouble(lonlats[0]);
                            lat = Double.parseDouble(lonlats[1]);
                            //设置缓存数据
                            settingPpMapCache(device.getId(), currentTimestamp, lon, lat, ppMap, wifis, objectMapper);
                        } else {
                            log.error("碰碰交好友建立连接时，百度定位失败了");
                            //碰碰交友失败
                            sendPpMakeFriendStatus(device.getId(), new JSONObject(), 0);
                        }
                    } else {
                        //没找到2个wifi的mac地址相同的记录
                        isFindFriend = ppMakeFriendByWifi(device, ppMap, wifis, isFindFriend, currentTimestamp);
                        if (!isFindFriend) {
                            //定位比较距离
                            String lonLatResult = bdAnalysisLocation(device.getId(), wifis, cells, cdma.toString(), message.getInteger("kafkaModelType"));
                            if (StringUtils.isNotEmpty(lonLatResult)) {
                                String[] lonlats = lonLatResult.split(",");
                                lon = Double.parseDouble(lonlats[0]);
                                lat = Double.parseDouble(lonlats[1]);
                                //根据经纬度计算距离来进行碰碰交友
                                isFindFriend = ppMakeFrendByLonLat(device, ppMap, lon, lat, isFindFriend, currentTimestamp);
                                if (!isFindFriend) {
                                    //设置缓存数据
                                    settingPpMapCache(device.getId(), currentTimestamp, lon, lat, ppMap, wifis, objectMapper);
                                }
                            } else {
                                log.error("碰碰交好友建立连接时，百度定位失败了");
                                //碰碰交友失败
                                sendPpMakeFriendStatus(device.getId(), new JSONObject(), 0);
                            }
                        }
                    }
                } catch (JsonProcessingException je) {
                    log.error("获取碰碰交好友的缓存数据失败，出现异常，异常原因：{}", je.getMessage());
                } finally {
                    redisPPMFAndWeatherService.unlock(lockKey, lockValue);
                }
            } else {
                //海外设备暂不处理
                log.error("系统暂时无法处理海外定位......");
                //发送碰碰交友失败的应答
                sendPpMakeFriendStatus(device.getId(), new JSONObject(), 0);
            }
        }
    }

    /**
     * @param [device, ppMap, wifis, isFindFriend, currentTimestamp]
     * @return boolean
     * @description 碰碰交友-利用wifi的mac地址找朋友
     * @author cwj
     * @date @time 2025/7/4 14:19
     **/
    private boolean ppMakeFriendByWifi(Device device, Map<String, Object> ppMap, JSONArray wifis, boolean isFindFriend, Long currentTimestamp) {
        //没找到2个wifi的mac地址相同的记录
        Iterator<Map.Entry<String, Object>> iterator = ppMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Object> entry = iterator.next();
            String imei = entry.getKey();
            Map<String, Object> innerMap = (Map<String, Object>) entry.getValue();
            if (ObjectUtils.isNotEmpty(innerMap.get("wifi"))) {
                int macCommCount = 0;
                //比对mac地址是否存在两个及以上相等，存在则认为是在一个地方
                Object wifiList = innerMap.get("wifi");
                JSONArray beforeWifis = new JSONArray();
                if (wifiList instanceof List) {
                    beforeWifis = new JSONArray((List<Object>) wifiList);
                }
                for (int i = 0; i < wifis.size(); i++) {
                    JSONObject wJson = wifis.getJSONObject(i);
                    for (int j = 0; j < beforeWifis.size(); j++) {
                        JSONObject bJson = beforeWifis.getJSONObject(j);
                        if (wJson.getString("mac").equals(bJson.getString("mac"))) {
                            macCommCount++;
                            break;
                        }
                    }
                    //有两个mac地址相同，则任务二者距离相近
                    if (macCommCount >= 2) {
                        isFindFriend = true;
                        //处理碰碰交好友的逻辑
                        dealwithPpMakeFriend(device, imei, currentTimestamp);
                        break;
                    }
                }
            }
        }
        return isFindFriend;
    }


    /**
     * @param [device, ppMap, lon, lat, isFindFriend, currentTimestamp]
     * @return boolean
     * @description 碰碰交友-用经纬度进行比找朋友
     * @author cwj
     * @date @time 2025/7/4 14:13
     **/
    private boolean ppMakeFrendByLonLat(Device device, Map<String, Object> ppMap, double lon, double lat, boolean isFindFriend, Long currentTimestamp) {
        //遍历比对
        Iterator<Map.Entry<String, Object>> iterator = ppMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Object> entry = iterator.next();
            String imei = entry.getKey();
            Map<String, Object> innerMap = (Map<String, Object>) entry.getValue();
            double longitude = (double) innerMap.get("longitude");
            double latitude = (double) innerMap.get("latitude");
            //利用经纬度计算二者距离
            double distince = PositionUtil.getDistance(longitude, latitude, lon, lat);
            if (distince < 300) {
                isFindFriend = true;
                //进行碰碰交友的处理
                dealwithPpMakeFriend(device, imei, currentTimestamp);
                //处理完之后，去掉处理过的
                iterator.remove();
                break;
            }
        }
        return isFindFriend;
    }

    /**
     * @param [device, imei, isFindFriend, currentTimestamp]
     * @return boolean
     * @description 碰碰交友处理，下发碰碰交友的应答，保存碰碰交友的数据
     * @author cwj
     * @date @time 2025/7/4 14:07
     **/
    private void dealwithPpMakeFriend(Device device, String imei, Long currentTimestamp) {
        Map<String, Map<String, Object>> ppFriendOuterMapA = device.getPpFriends();
        Device deviceB = this.deviceService.findById(imei);
        Map<String, Map<String, Object>> ppFriendOuterMapB = deviceB.getPpFriends();
        boolean isAContainB = !MapUtils.isEmpty(ppFriendOuterMapA) && !MapUtils.isEmpty(ppFriendOuterMapA.get(imei));
        boolean isBContainA = !MapUtils.isEmpty(ppFriendOuterMapB) && !MapUtils.isEmpty(ppFriendOuterMapB.get(device.getId()));
        JSONObject kafkaJson = new JSONObject();
        if (isAContainB || isBContainA) {
            if (isAContainB) {
                //告知当前设备，对方已经在自己的好友列表
                sendPpMakeFriendStatus(device.getId(), kafkaJson, 2);
                //告知对方设备，你（对方设备）已经在它（当前设备）的好友列表中了
                // sendPpMakeFriendStatus(imei, kafkaJson,1);
                sendPpMakeFriendStatus(imei, kafkaJson, 2);
            }
            if (isBContainA) {
                //告知对当前设备，自己已经在对方好友列表中了
                //sendPpMakeFriendStatus(device.getId(), kafkaJson,1);
                sendPpMakeFriendStatus(imei, kafkaJson, 2);
                //告知对方设备，对方设备的还有列表中已经有了
                sendPpMakeFriendStatus(imei, kafkaJson, 2);
            }
        } else {
            //下发指令
            String onlineStatus1 = redisClusterService.hget(imei, ConstCacheField.ONLINE_STATUS);
            String onlineStatus2 = redisClusterService.hget(device.getId(), ConstCacheField.ONLINE_STATUS);
            if ("1".equals(onlineStatus1) && "1".equals(onlineStatus2)) {
                if (ObjectUtils.isEmpty(ppFriendOuterMapA)) {
                    ppFriendOuterMapA = new HashMap<>();
                }
                if (ObjectUtils.isEmpty(ppFriendOuterMapB)) {
                    ppFriendOuterMapB = new HashMap<>();
                }
                //更新好友设备的好友列表
                Map<String, Object> ppFriendInnerMapA = new HashMap<>();
                ppFriendInnerMapA.put("t", currentTimestamp);
                ppFriendOuterMapA.put(imei, ppFriendInnerMapA);
                Map<String, Object> ppFriendInnerMapB = new HashMap<>();
                ppFriendInnerMapB.put("t", currentTimestamp);
                ppFriendOuterMapB.put(device.getId(), ppFriendInnerMapB);
                Update updateA = new Update();
                Update updateB = new Update();
                updateA.set("pf", ppFriendOuterMapA);
                updateB.set("pf", ppFriendOuterMapB);
                deviceService.updateById(device.getId(), updateA);
                deviceService.updateById(imei, updateB);
                //创建设备的单聊群组
                deviceSingleChatGroupService.createDeviceSingleGroup(device.getId(), imei);
                assmbleKafkaJson(kafkaJson, imei, deviceB);
                log.info("下发了碰碰交友的指令，下发指令数据为：{}，下发的到的设备为:{}", kafkaJson, device.getId());
                kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_PP_FRIEND_RESP, kafkaJson, null);
                assmbleKafkaJson(kafkaJson, device.getId(), device);
                log.info("下发了碰碰交友的指令，下发指令数据为：{}，下发的到的设备为:{}", kafkaJson, imei);
                kafkaService.emitDeviceDemand(imei, ConstKafka.DEVICE_TYPE_PP_FRIEND_RESP, kafkaJson, null);
                //构建
            } else {
                log.error("碰碰交友失败，设备不在线，请将两设备连上网络之后再操作");
                sendPpMakeFriendStatus(device.getId(), kafkaJson, 0);
            }

        }
    }


    /**
     * @param [device, kafkaJson, status]
     * @return void
     * @description 发送碰碰交好友的状态指令
     * @author cwj
     * @date @time 2025/7/4 17:09
     **/
    private void sendPpMakeFriendStatus(String imei, JSONObject kafkaJson, Integer status) {
        kafkaJson.put("status", status.toString());
        kafkaService.emitDeviceDemand(imei, ConstKafka.DEVICE_TYPE_PP_FRIEND_STATUS_RESP, kafkaJson, null);
    }


    /**
     * @param [kafkaJson, imei, deviceB]
     * @return void
     * @description 组装下发数据的kafka对象
     * @author cwj
     * @date @time 2025/7/4 14:03
     **/
    private void assmbleKafkaJson(JSONObject kafkaJson, String imei, Device device) {
        kafkaJson.put("id", imei.substring(4, 14));
        kafkaJson.put("phone", device.getPhone());
        kafkaJson.put("name", device.getName());
        if (StringUtil.isNotBlank(device.getImage()) && !device.getImage().contains("default.jpg")) {
            kafkaJson.put("avator", "/" + BaseBucketTool.DEVICE_COMMON_PATH + device.getImage());
        }
    }


    /**
     * @param [lockKey, lockValue]
     * @return boolean
     * @description 竞争redis锁资源
     * @author cwj
     * @date @time 2025/7/3 17:46
     **/
    private boolean lockedRedis(String lockKey, String lockValue) {
        boolean locked = false;
        int retryTimes = 3000;
        int retryIntervalMs = 50;
        for (int i = 0; i < retryTimes; i++) {
            locked = redisPPMFAndWeatherService.tryLock(lockKey, lockValue, 5000);
            if (locked) {
                break;
            }
            try {
                Thread.sleep(retryIntervalMs); //睡眠一会再试
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.error("休眠程序时出现异常，异常原因：{}", e.getMessage());
            }
        }
        return locked;
    }

    /**
     * @param [imei, wifis, cells, cdma, mcc, kafkaModelType]
     * @return java.lang.String
     * @description 获取经纬度字符串，中间用英文逗号隔开
     * @author cwj
     * @date @time 2025/7/3 17:11
     **/
    private String bdAnalysisLocation(String imei, JSONArray wifis, JSONArray cells, String cdma, Integer kafkaModelType) {
        String lonLatResult = "";
        JSONObject param = new JSONObject();
        String ak = listDataProperties.getAmapBdWebLBSKey().get(RandomUtil.getRandomInt(0, listDataProperties.getAmapBdWebLBSKey().size()));
        //初始化请求参数的基础参数
        initRequestParam(param, ak);
        List<JSONObject> body = new ArrayList<>();
        JSONObject paramObj = new JSONObject();
        //优先级 WIFI > CELLS > TELECOMCELL
        if ("1".equals(cdma) && cells != null && cells.size() > 0) {
            JSONObject cell = cells.getJSONObject(0);
            if (cell != null && cell.size() >= 3) {
                //CDMA 格式为:sid,nid,bid,lon,lat,signal， 其中 lon,lat, signal 可为空，格式为:sid,nid,bid,,,signal
                paramObj.put("accesstype", 0);
                cdma = "1";
                String sid = cell.getString("sid");
                String nid = cell.getString("nid");
                String bid = cell.getString("bid");
                if (!"0".equals(sid) && !"0".equals(nid) && !"0".equals(bid)) {
                    StringBuilder bts = new StringBuilder();
                    bts.append(sid).append(",").append(nid).append(",").append(bid).append(",");
                    if (cell.containsKey("lon")) {
                        bts.append(cell.get("lon"));
                    }
                    bts.append(",");
                    if (cell.containsKey("lat")) {
                        bts.append(cell.get("lat"));
                    }
                    bts.append(",");
                    if (cell.containsKey("signal")) {
                        bts.append(cell.get("signal"));
                    }
                    paramObj.put("bts", bts.toString());
                }
            }
        }
        if (cells != null && cells.size() > 0) {
            //非 CDMA 格式为:mcc,mnc,lac,cellid,signal
            if (cells.size() == 1) {
                //如果只上传了一个基站,那么应该是 4G基站, 使用LTE
                paramObj.put("network", "LTE");
            }
            paramObj.put("accesstype", 0);
            JSONObject cellIndex1 = cells.getJSONObject(0);
            int rxlev1 = TCPUtil.rxlevHandle(cellIndex1.get("rxlev"));
            cellIndex1.put("rxlev", rxlev1);
            rxlev1 = Math.abs(rxlev1);
            //用户所属国家代码
            //识别不到默认中国
            Integer mcc = 460;
            if (cellIndex1.containsKey("mcc")) {
                mcc = Integer.valueOf(cellIndex1.get("mcc").toString());
            }
            paramObj.put("bts", String.format("%s,%s,%s,%s,%s", mcc, cellIndex1.get("mnc"),
                    cellIndex1.get("lac"), cellIndex1.get("ci"), cellIndex1.get("rxlev")));
            cdma = "0";
            if (cells.size() > 1) {
                StringBuilder nearbtsStr = new StringBuilder();
                for (int i = 0; i < cells.size(); i++) {
                    JSONObject ce = cells.getJSONObject(i);
                    int rx = TCPUtil.rxlevHandle(ce.get("rxlev"));
                    ce.put("rxlev", rx);
                    //用户所属国家代码
                    Integer tempMcc = Integer.valueOf(ce.get("mcc").toString());
                    if (tempMcc == 0) {
                        //识别不到默认中国
                        tempMcc = 460;
                    }
                    if (Math.abs(rx) < rxlev1) {
                        rxlev1 = Math.abs(rx);
                        paramObj.put("bts", String.format("%s,%s,%s,%s,%s", tempMcc, ce.get("mnc"),
                                ce.get("lac"), ce.get("ci"), ce.get("rxlev")));
                    }
                    nearbtsStr.append(String.format("%s,%s,%s,%s,%s|", tempMcc, ce.get("mnc"),
                            ce.get("lac"), ce.get("ci"), ce.get("rxlev")));
                }
                try {
                    //log.info("nearbts数据为："+nearbtsStr.substring(0, nearbtsStr.length() - 1));
                    //paramObj.put("nearbts", URLEncoder.encode(nearbtsStr.substring(0, nearbtsStr.length() - 1), "UTF-8"));
                    paramObj.put("nearbts", nearbtsStr.substring(0, nearbtsStr.length() - 1));
                } catch (Exception e) {
                    log.error("百度地图LBS解析出现异常，异常信息为：{}", e.getMessage());
                }
            }
        }
        if (wifis != null && wifis.size() > 0) {
            paramObj.put("accesstype", 1);
            StringBuilder wifiStr = new StringBuilder();
            if (filterWifiVendorModelService != null) {
                FilterWifiVendorModel vendorModel = filterWifiVendorModelService.findById("mianConfig");
                for (int i = 0; i < wifis.size(); i++) {
                    JSONObject wifi = wifis.getJSONObject(i);
                    if (StringUtil.isNotBlank(wifi.getString("mac"))) {
                        String macBefore6 = wifi.getString("mac").replace(":", "");
                        if (macBefore6.length() > 6) {
                            macBefore6 = macBefore6.substring(0, 6).toUpperCase();
                            MacToCompany macToCompany = macToCompanyService.findById(macBefore6);
                            if (macToCompany != null && StringUtil.isNotBlank(macToCompany.getCompanyName())) {
                                String companyName = macToCompany.getCompanyName().toUpperCase();
                                boolean isFilter = false;
                                for (String moblieType : vendorModel.getMoblieType()) {
                                    if (companyName.contains(moblieType)) {
                                        // 公司名中存在数据库中存的过滤的手机厂商的名字的,跳过本轮
                                        log.info("Mac 地址被过滤 = {} 过滤的厂商是={}", wifi.get("mac"), moblieType);
                                        isFilter = true;
                                        break;
                                    }
                                }
                                if (isFilter) {
                                    continue;
                                }
                            }
                        }
                    }
                    int newSignal = 0;
                    if (StringUtil.isNumeric(wifi.get("signal"))) {
                        newSignal = new Double(wifi.getString("signal")).intValue();
                        if (newSignal > 0) {
                            newSignal = 2 * newSignal - 113;
                        }
                    }
                    if (StringUtil.isNotBlank(wifi.get("connected")) && "1".equals(wifi.getString("connected"))) {
                        paramObj.put("mmac", String.format("%s,%s,%s", wifi.get("mac"), newSignal, wifi.get("ssid")));
                    }
                    wifiStr.append(String.format("%s,%s,%s|", wifi.get("mac"), newSignal, wifi.get("ssid")));
                }
            }
            if (wifiStr.length() > 0) {
                try {
                    //log.info("macs数据为："+wifiStr.substring(0, wifiStr.length() - 1));
                    //paramObj.put("macs", URLEncoder.encode(wifiStr.substring(0, wifiStr.length() - 1), "UTF-8"));
                    paramObj.put("macs", wifiStr.substring(0, wifiStr.length() - 1));
                } catch (Exception e) {
                    log.error("百度地图定位发生异常，异常信息为：{}", e.getMessage());
                }
            }
        }
        //设置body参数的内容
        settingBodyParam(paramObj, imei, cdma);
        String imsi = RedisAccountClusterService.getJedisCluster().hget(imei, ConstCacheField.IMSI);
        if (StringUtil.isNotBlank(imsi)) {
            paramObj.put("imsi", imsi);
        }
        body.add(paramObj);
        param.put("body", body);
        long BDstartFlagTime = System.currentTimeMillis();
        String requestURL = "https://api.map.baidu.com/locapi/v2";
        try {
            log.info("百度地图请求参数:{}", param);
            JSONObject responseJsonObject = HttpReq.sendBdLBSLocatePost(requestURL, param);
            if (ObjectUtils.isNotEmpty(responseJsonObject) && responseJsonObject.getInteger("errcode").equals(0)) {
                log.info("imei: {} 百度地图位置解析 time={} kafkaModelType;{}", imei, System.currentTimeMillis() - BDstartFlagTime, kafkaModelType);
                JSONArray bodyArray = responseJsonObject.getJSONArray("body");
                JSONObject bodyObj = bodyArray.getJSONObject(0);
                String[] loactions = bodyObj.getString("location").split(",");
                lonLatResult = loactions[0] + "," + loactions[1];

            }
        } catch (Exception e) {
            log.error("碰碰交友时，利用百度地图定位发生异常，异常信息为：{}", e.getMessage());
        }
        return lonLatResult;
    }


    /**
     * @param [cells, cdma, wifis]
     * @return boolean
     * @description 检查数据是否合规
     * @author cwj
     * @date @time 2025/7/3 16:29
     **/
    private boolean checkIsParsing(JSONArray cells, Integer cdma, JSONArray wifis) {
        boolean parsing = false;
        if ((cells != null && cells.size() > 0 && cdma == 0) || (wifis != null && wifis.size() >= 2)) {
            parsing = true;
            if (cells != null && cells.size() > 0 && cdma == 0) {
                if (cells.size() == 1) {
                    JSONObject item = cells.getJSONObject(0);
                    if (item.getInteger("mcc") == 0 &&
                            item.getInteger("mnc") == 0 &&
                            item.getInteger("ci") == 0) {
                        parsing = false; //不合规
                    }
                }
            }
        } else if (cdma == 1 && cells != null && cells.size() > 0) {
            JSONObject cell = cells.getJSONObject(0);
            if (cell.get("sid") != null && !"0".equals(cell.get("sid").toString())
                    && cell.get("nid") != null && !"0".equals(cell.get("nid").toString())
                    && cell.get("bid") != null && !"0".equals(cell.get("bid").toString())) {
                parsing = true;
            }
        }
        return parsing;
    }

    /**
     * @param [imei, currentTimestamp, lon, lat, ppMap, objectMapper]
     * @return void
     * @description 重写缓存数据
     * @author cwj
     * @date @time 2025/7/3 15:50
     **/
    private void settingPpMapCache(String imei, Long currentTimestamp, double lon, double lat, Map<String, Object> ppMap, JSONArray wifi, ObjectMapper objectMapper) throws JsonProcessingException {
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("ts", currentTimestamp.toString());
        dataMap.put("longitude", lon);
        dataMap.put("latitude", lat);
        if (!ObjectUtils.isEmpty(wifi) && !wifi.isEmpty()) {
            dataMap.put("wifi", wifi);
        }
        ppMap.put(imei, dataMap);
        redisPPMFAndWeatherService.set(Constant.PP_MAKE_FRIEND_KEY, objectMapper.writeValueAsString(ppMap));
    }

    /**
     * @param []
     * @return java.util.Map
     * @description 取碰碰交友缓存的Map集合，移除超时的数据，获取缓存中5分钟以内的数据
     * @author cwj
     * @date @time 2025/7/3 9:40
     **/
    public Map<String, Object> getRedisPpFriendsData(ObjectMapper objectMapper, String imeiself) throws JsonProcessingException {
        //缓存碰碰交友的数据
        String ppFriendsDatas = redisPPMFAndWeatherService.get(Constant.PP_MAKE_FRIEND_KEY);
        if (StringUtils.isNotEmpty(ppFriendsDatas)) {
            Map<String, Object> ppMap = objectMapper.readValue(ppFriendsDatas, new TypeReference<Map<String, Object>>() {
            });
            if (MapUtils.isEmpty(ppMap)) {
                return null;
            } else {
                //去除ppMap中超过5分钟的数据
                Iterator<Map.Entry<String, Object>> iterator = ppMap.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<String, Object> entry = iterator.next();
                    String imei = entry.getKey();
                    //去掉自己添加的缓存记录
                    if (imei.equals(imeiself)) {
                        iterator.remove();
                        continue;
                    }
                    Object value = entry.getValue();
                    if (!(value instanceof Map)) {
                        iterator.remove();
                        continue;
                    }
                    Map<String, Object> innerMap = (Map<String, Object>) entry.getValue();
                    long timestamp = Long.parseLong(innerMap.get("ts").toString());
                    long currentTime = System.currentTimeMillis() / 1000;
                    //移除超过300s的记录
                    if (currentTime - timestamp > 300) {
                        //下发交友失败的回复指令，都超过5分钟仍未完成交友
                       /* JSONObject kafkaJson = new JSONObject();
                        kafkaJson.put("status",0);
                        kafkaService.emitDeviceDemand(imei,ConstKafka.DEVICE_TYPE_PP_FRIEND_STATUS_RESP,kafkaJson,null);*/
                        iterator.remove();
                    }
                }
                return ppMap;
            }
        } else {
            return null;
        }
    }

    /**
     * 处理定位指令
     *
     * @param device
     * @param message
     * @time: 2021/1/21 11:28
     * @author: Rock
     * @version 1.0
     */
    private void type_7(Device device, JSONObject message) {
        //log.info("进入了定位解析方法......");
        //判断是否需要反馈指令 且是 普通定位的kafka消费模式模式, 先回复一下
        if (message.containsKey("feedback") && !"0".equals(message.get("feedback").toString())) {
            if (message.getInteger("kafkaModelType") == 0) {
                feedbackFunc(device.getId(), null, message, false);
            }
        }
        if (!StringUtil.isNumeric(message.get("location_time"))) {
            log.error("定位点缺少时间戳 imei={} ", device.getId());
            return;
        }
        //有定位补传的指令,这个不进行过滤
//        long location_time = new Double(Double.parseDouble(message.get("location_time").toString())).longValue();
//        if (Math.abs(location_time - System.currentTimeMillis() / 1000) > Const.ONE_DAY_TIME) {
//            //如果这些点不正常(和当前时间的相差超过一天的时间)
//            log.warn("定位点上传的时间戳有误 imei={} message={}", device.getId(), message);
//            return;
//        }
        //保存定位原始数据（每天只保存一条，三天后就过期）
        //deviceLocationOriginalRecordService.saveOrUpdateData(device.getId(),message);
        //保存定位原始数据（来了就保存，三天过期）
        deviceLocationOriginalRecordService.saveOriginalLocData(device.getId(), message);
        //如果上传的字段中包含有电量,那么也更新电量,不进行电量省电等状态处理
        if (message.get("battery") != null) {
            type_battery(device, message);
        }

        if (message.get("stepNumber") != null) {
            message.put("number", message.get("stepNumber"));
            type_step(device, message);
        }

        if (message.get("rollNumber") != null) {
            message.put("number", message.get("rollNumber"));
            type_rollNum(device, message);
        }

        //有定位补传的指令,这个不进行过滤
//        if (device.getNowloc() != null && device.getNowloc().size() > 0
//                && StringUtil.isNumeric(device.getNowloc().get("timestamp"))) {
//            long locTimestamp = new Double(Double.parseDouble(device.getNowloc().get("timestamp").toString())).longValue();
//            if (locTimestamp > location_time) {
//                log.info("过期的定位点 imei={} ", device.getId());
//                return;
//            }
//        }
        boolean isGPS = false;
        String locationType = "";
        JSONObject addLBSDict = null;
        JSONObject addLBSResultDict = null;
        JSONObject addGPSDict = null;
        int useBgAddressTips = 0;

        double lon = 0;
        double lat = 0;
        int radius = 0;
        if (message.containsKey("gps")) {
            //设置GPS标识
            isGPS = true;
            locationType = "GPS";
            //GPS定位解析方法 默认不解析, 等需要用到的时候再解析
//            addGPSDict = toGPSparsing(message.getJSONObject("gps"), device, message);
            /**
             * 坐标转换程序
             * <p>
             * WGS84坐标系：即地球坐标系，国际上通用的坐标系。Earth
             * <p>
             * GCJ02坐标系：即火星坐标系，WGS84坐标系经加密后的坐标系。Mars
             * <p>
             * BD09坐标系：即百度坐标系，GCJ02坐标系经加密后的坐标系。 Bd09
             * <p>
             * 搜狗坐标系、图吧坐标系等，估计也是在GCJ02基础上加密而成的。
             * <p>
             * 百度地图API 百度坐标 腾讯搜搜地图API 火星坐标 搜狐搜狗地图API 搜狗坐标* 阿里云地图API 火星坐标 图吧MapBar地图API 图吧坐标
             * 高德MapABC地图API 火星坐标 灵图51ditu地图API 火星坐标
             */

            //进行一下坐标转换
            addGPSDict = message.getJSONObject("gps");
            double[] changeGPS = CoordinateConvert.wgs2GCJ(addGPSDict.getDouble("lat"), addGPSDict.getDouble("lon"));
            addGPSDict.put("lat", changeGPS[0]);
            addGPSDict.put("lon", changeGPS[1]);
            lat = changeGPS[0];
            lon = changeGPS[1];
            if (addGPSDict.containsKey("radius")) {
                radius = addGPSDict.getInteger("radius");
            }
            //将坐标利用百度定位进行逆地址解析
           /* Map<String, Object> paramMap = new LinkedHashMap<>();
            String[] loactions = new String[2];
            loactions[0] = lat + "";
            loactions[1] = lon + "";
            String ak = listDataProperties.getAmapBdWebLBSKey().get(RandomUtil.getRandomInt(0, listDataProperties.getAmapBdWebLBSKey().size()));
            //设置逆编码请求参数
            settingReserseGeocodingParam(paramMap, ak, loactions);
            //让其返回详细地址
            paramMap.put("extensions_poi",1);
            log.info("GPS定位，逆地址解析参数：{}",paramMap);
            JSONObject inverAddrJsonObj = HttpReq.sendBdGet("https://api.map.baidu.com/reverse_geocoding/v3?",paramMap);
            log.info("GPS定位，逆地址解析返回数据：{}",inverAddrJsonObj);
            if(ObjectUtils.isNotEmpty(inverAddrJsonObj) && inverAddrJsonObj.getInteger("status").equals(0)){
                JSONObject resultData = inverAddrJsonObj.getJSONObject("result");
                if(ObjectUtils.isNotEmpty(resultData)) {
                    if(ObjectUtils.isNotEmpty(resultData.get("formatted_address_poi"))) {
                        addGPSDict.put("formatted_address",resultData.get("formatted_address_poi"));
                    } else if(ObjectUtils.isNotEmpty(resultData.get("formatted_address"))) {
                        addGPSDict.put("formatted_address",resultData.get("formatted_address"));
                    }
                    if(ObjectUtils.isNotEmpty(resultData.get("addressComponent"))) {
                        addGPSDict.put("addressComponent",resultData.getJSONObject("addressComponent"));
                    }
                }
            }*/
        } else {
            //基站信息解析
            JSONObject resultJson = new JSONObject();
            //调用统一方法后,分析resultJson
            /* resultJson 格式
            {
                "data": {
                    "result": {
                        "country": "中国",
                        "province": "广东省",
                        "citycode": "0755",
                        "city": "深圳市",
                        "adcode": "440305",
                        "road": "茶光路",
                        "street": "茶光路",
                        "location": "113.9391249,22.5684901",
                        "poi": "波顿科技园",
                        "type": "4",
                        "radius": "550",
                        "desc": "广东省 深圳市 南山区 茶光路 靠近波顿科技园"
                    },
                    "infocode": "10000",
                    "info": "OK",
                    "status": "1"
                },
                "locationType": "LBS",
                "useBgAddressTips": 0
            }
            */
            //定位处理
            executionLocateFuntion(device.getId(), message, device, resultJson);
            locationType = resultJson.getString("locationType");
            useBgAddressTips = resultJson.getInteger("useBgAddressTips");
            //包含定位信息的result
            addLBSDict = resultJson.getJSONObject("data");
            if (addLBSDict != null) {
                boolean isFail = false;
                if (INFO_CODE.equals(addLBSDict.getString("infocode"))) {
                    //含有result的定位信息
                    addLBSResultDict = addLBSDict.getJSONObject("result");
                    if (addLBSResultDict != null) {
                        if (StringUtil.isNotBlank(addLBSResultDict.get("location"))) {
                            String[] locationArr = addLBSResultDict.getString("location").split(",");
                            lon = Double.parseDouble(locationArr[0]);
                            lat = Double.parseDouble(locationArr[1]);
                        } else {
                            isFail = true;
                        }
//                        //没有 desc 或 没有 city 进行逆地理位置编码
//                        if (!addLBSResultDict.containsKey("desc") || !addLBSResultDict.containsKey("city")) {
//                            JSONObject requesData = revertLonLat(lon, lat);
//                            if (requesData != null && INFO_CODE.equals(requesData.getString("infocode"))) {
//                                addGPSDict = requesData.getJSONObject("regeocode");
//                            }
//                        }
                    } else {
                        isFail = true;
                    }
                } else {
                    isFail = true;
                }
                if (isFail) {
                    log.warn("解析 基站WIFI数据 失败 imei:{} addLBSDict:{}", device.getId(), addLBSDict);
                    //判断是否反馈指令
                    if (message.containsKey("feedback") && message.getInteger("feedback") != 0) {
                        //如果是 普通定位的kafka消费模式模式, 上面已提前回复了,这里就不进行回复了
                        if (message.getInteger("kafkaModelType") != 0) {
                            feedbackFunc(device.getId(), null, message, isGPS);
                        }
                    }
                    return;
                }
            } else {
                //解析 基站WIFI数据 失败
                log.warn("解析 基站WIFI数据 失败 imei:{}", device.getId());
                //判断是否反馈指令
                if (message.containsKey("feedback") && message.getInteger("feedback") != 0) {
                    //如果是 普通定位的kafka消费模式模式, 上面已提前回复了,这里就不进行回复了
                    if (message.getInteger("kafkaModelType") != 0) {
                        feedbackFunc(device.getId(), null, message, isGPS);
                    }
                }
                return;
            }
        }
        JSONObject kafkaPushJson = new JSONObject();
        kafkaPushJson.put("type", ConstKafka.DEVICE_NOW_LOCATION);
        kafkaPushJson.put("imei", device.getId());
        kafkaPushJson.put("lon", lon);
        kafkaPushJson.put("lat", lat);
        kafkaPushJson.put("useBgAdd", useBgAddressTips);
        kafkaPushJson.put("timestamp", message.get("location_time"));
        //添加一下电量
        kafkaPushJson.put("battery", message.get("battery"));
        kafkaPushJson.put("loc_type", locationType);

        boolean isUpdateNowLoc = true;
        if (device.getNowloc() != null && device.getNowloc().size() > 0 && StringUtil.isNumeric(device.getNowloc().get("timestamp"))) {
            long locTimestamp = new Double(Double.parseDouble(device.getNowloc().get("timestamp").toString())).longValue();
            long location_time = new Double(Double.parseDouble(message.get("location_time").toString())).longValue();
            if (locTimestamp > location_time) {
                log.info("以前的定位点,可能是补传的定位点, 不更新nowLoc imei={} ", device.getId());
                isUpdateNowLoc = false;
            }
        }
        if (isUpdateNowLoc) {
            device.setNowloc(new HashMap<>());
            device.getNowloc().put("type", "deviceNowLocation");
            device.getNowloc().put("imei", device.getId());
            device.getNowloc().put("lon", lon);
            device.getNowloc().put("lat", lat);
            device.getNowloc().put("timestamp", message.get("location_time"));
            device.getNowloc().put("loctype", locationType);
            if (isGPS) {
                device.getNowloc().put("radius", radius);
                //kafkaPushJson.put("radius", "0");
                kafkaPushJson.put("radius", radius);
                if (useBgAddressTips == 0) {
                    device.getNowloc().put("address", addGPSDict.get("formatted_address"));
                }
                //存一个比较模糊的地址
                device.getNowloc().put("fuzzyAdd", addGPSDict.get("formatted_address"));
            } else {
                if (addLBSResultDict != null) {
                    device.getNowloc().put("radius", addLBSResultDict.get("radius"));
                    kafkaPushJson.put("radius", addLBSResultDict.get("radius"));
                    if (useBgAddressTips == 0) {
                        if (addLBSResultDict.containsKey("desc")) {
                            device.getNowloc().put("address", addLBSResultDict.get("desc"));
                        } else {
                            if (addGPSDict != null) {
                                device.getNowloc().put("address", addGPSDict.get("formatted_address"));
                            }
                        }
                    }
                    // 存一个比较模糊的地址
                    if (addLBSResultDict.containsKey("desc")) {
                        device.getNowloc().put("fuzzyAdd", addLBSResultDict.get("desc"));
                    } else {
                        if (addGPSDict != null) {
                            device.getNowloc().put("fuzzyAdd", addGPSDict.get("formatted_address"));
                        }
                    }
                }
            }
            device.getNowloc().put("useBgAdd", useBgAddressTips);
            //更新设备最新的位置信息
            deviceService.updateById(device.getId(), new Update().set("nowloc", device.getNowloc()));
        }
//        //最近一次定位时间记录在 redis 中
//        Double[] lonAndLat = {lon, lat};
//        RedisClusterService.getJedisCluster().hset(device.getId(), ConstCacheField.LNGLAT, lonAndLat.toString());
//        //定位时间
//        RedisClusterService.getJedisCluster().hset(device.getId(), ConstCacheField.LNGLAT_TIME, message.getString("location_time"));
//        //定位位置解析
//        RedisClusterService.getJedisCluster().hset(device.getId(), ConstCacheField.LNGLAT_ADDRESS, String.valueOf(device.getNowloc().get("address")));
        kafkaPushJson.put("useBgAdd", useBgAddressTips);
        if (message.getInteger("kafkaModelType") == 1) {
            if (CollectionUtil.isNotEmpty(device.getUserMap())) {
                for (String openid : device.getUserMap().keySet()) {
                    String openidHuancun = RedisClusterService.getJedisCluster().get(CacheEnum.RequestLocationIdent.getKey() + device.getId() + ":" + openid);
                    if (StringUtil.isNotBlank(openidHuancun)) {
                        kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_3, openid, kafkaPushJson);
                        RedisClusterService.getJedisCluster().del(CacheEnum.RequestLocationIdent.getKey() + device.getId() + ":" + openid);
                        break;
                    }
                }
            }
        }
        if (message.containsKey("feedback") && !"0".equals(message.getString("feedback"))) {
            JSONObject tempJson = null;
            if (isGPS) {
                tempJson = addGPSDict;
            } else {
                tempJson = addLBSDict.getJSONObject("result");
            }
            //如果是 普通定位的kafka消费模式模式, 上面已提前回复了,这里就不进行回复了
            if (message.getInteger("kafkaModelType") != 0) {
                feedbackFunc(device.getId(), tempJson, message, isGPS);
            }
        }
        //设备轨迹信息
        DeviceLocation newLoc = new DeviceLocation();
        newLoc.setLon(String.valueOf(lon));
        newLoc.setLat(String.valueOf(lat));
        newLoc.setTime(StringUtil.isNumeric(message.get("location_time")) ? Double.parseDouble(message.getString("location_time")) : 0d);
        newLoc.setUseBgAdd(useBgAddressTips);
        newLoc.setType(locationType);
        if (isGPS) {
            newLoc.setRadius("0");
            if (addGPSDict.containsKey("formatted_address")) {
                //不设置地理位置,让APP端解析地理位置
                newLoc.setAddr(addGPSDict.getString("formatted_address"));
                JSONObject addressComponent = addGPSDict.getJSONObject("addressComponent");
                if (addressComponent != null) {
                    newLoc.setAdcode(addressComponent.getString("adcode"));
                    newLoc.setCity(addressComponent.getString("city"));
                }
            }
        } else {
            //取定位结果信息
            JSONObject reJson = addLBSDict.getJSONObject("result");
            newLoc.setAdcode("");
            newLoc.setCity("");
            newLoc.setAddr("");
            if (reJson != null) {
                newLoc.setRadius(reJson.getString("radius"));
                if (reJson.containsKey("desc")) {
                    newLoc.setAddr(reJson.getString("desc"));
                }
                //if (reJson.containsKey("desc") && reJson.containsKey("city")) {
                if (reJson.containsKey("adcode") && reJson.containsKey("city")) {
                    //不设置地理位置,让APP端解析地理位置
//                    newLoc.setAddr(reJson.getString("desc"));
                    newLoc.setCity(reJson.getString("city"));
                    newLoc.setAdcode(reJson.getString("adcode"));
                } else {
                    if (addGPSDict != null && addGPSDict.containsKey("formatted_address")) {
                        //不设置地理位置,让APP端解析地理位置
//                        newLoc.setAddr(addGPSDict.getString("formatted_address"));
                        JSONObject addressComponent = addGPSDict.getJSONObject("addressComponent");
                        if (addressComponent != null) {
                            newLoc.setAdcode(addressComponent.getString("adcode"));
                            newLoc.setCity(addressComponent.getString("city"));
                        }
                    }
                }
            }
        }

        //boolean isAlreadyNewLoc = false;
        boolean isEffortLoaction = true;
        if ((Double.valueOf(0).equals(lon) && Double.valueOf(0).equals(lat)) || (Double.valueOf(-1).equals(lon) && Double.valueOf(-1).equals(lat))) {
            isEffortLoaction = false;
        }
        Document newLocData = null;
        //检查,SOS ,挂失, 安全围栏
        String locUpdateType = message.getString("locationType");
        if ("sos".equals(locUpdateType)) {
            //sos
            newLocData = pushSOSMsg(device, message, newLoc);
        } else if ("lossdev".equals(locUpdateType)) {
            //挂失
            newLocData = pushLossDevMsg(device, message, newLoc);
        } else {
            //是否进出 安全围栏 判断
            newLocData = checkRail(device, message, newLoc);
        }
        //生成的轨迹点插入数据库的值
        if (device.getLoc() == null || device.getLoc().size() <= 0) {
            //不存在初始值 生成轨迹点
            if (newLocData == null && isEffortLoaction) {
                newLocData = setNewloctionFunc(device, newLoc, "", message);
            }
        } else {
            //未生成轨迹点时进行常规轨迹点判断
            if (newLocData == null) {
                //判断是否生成常规轨迹点
                double lastlon = Double.parseDouble(StringUtil.isNumeric(device.getLoc().get("lon")) ? device.getLoc().get("lon").toString() : "0");
                double lastlat = Double.parseDouble(StringUtil.isNumeric(device.getLoc().get("lat")) ? device.getLoc().get("lat").toString() : "0");
                double distance = PositionUtil.getDistance(lastlon, lastlat, lon, lat);
                String td = DateUtil.dateToStr(new Date(), DateUtil.FORMAT_4);
                String lastD = "";
                long lastLocTime = 0;
                if (StringUtil.isNumeric(device.getLoc().get("t"))) {
                    lastLocTime = new Double(device.getLoc().get("t").toString()).longValue();
                    lastD = DateUtil.stampToDateStr(lastLocTime * 1000, DateUtil.FORMAT_4);
                }
                //上报的定位是有效的定位信息
                if (isEffortLoaction) {
                    // 大于一天 或 距离超过限定
                    if (System.currentTimeMillis() / 1000 - lastLocTime > 86400 || distance > Const.DISTANCE_RADIUS_WITH_LOC_POINT) {
                        newLocData = setNewloctionFunc(device, newLoc, "", message);
                    } else if (!td.equals(lastD)) {
                        //日期不一致 也生成轨迹点
                        newLocData = setNewloctionFunc(device, newLoc, td, message);
                    }
                }
            }
        }
        log.info("imei:{} 定位解析完成..", device.getId());
    }


    private Document pushSOSMsg(Device device, JSONObject message, DeviceLocation newLoc) {
        //设备的厂商型号
        String vendorCode = null;
        if (device.getAgent() != null) {
            vendorCode = device.getAgent().toString();
        }
        String deviceName = deviceService.getDeviceName(device);
        String msg = MessageUtils.getLocale(device.getLocale(), "API.pushTitle.SOS", deviceName);
//        String msg = String.format("【%s】设备发生SOS报警，查看位置", deviceName);
        String msgContent = msg;
        Document res = setNewloctionFunc(device, newLoc, "", message);
        Double lon = Double.valueOf(newLoc.getLon());
        Double lat = Double.valueOf(newLoc.getLat());
        DeviceMessage deviceMessage = new DeviceMessage();
        deviceMessage.setImei(device.getId());
        deviceMessage.setType("sos");
        deviceMessage.setName(deviceName);
        deviceMessage.setLat(lat);
        deviceMessage.setLon(lon);
        deviceMessage.setLoc_type(newLoc.getType());
        deviceMessage.setMsgTitle(msg);
        deviceMessage.setMsgContent(msgContent);
        deviceMessageService.save(deviceMessage);

        if (CollectionUtil.isNotEmpty(device.getUserMap())) {
            //发送设置推送
            JSONObject pushData = new JSONObject();
            JSONObject messageJson = new JSONObject();
            messageJson.put("imei", device.getId());
            messageJson.put("type", "sos");
            messageJson.put("name", deviceName);
            messageJson.put("lon", lon);
            messageJson.put("lat", lat);
            messageJson.put("loc_type", newLoc.getType());
            messageJson.put("msgTitle", msg);
            messageJson.put("msgContent", msgContent);
            messageJson.put("timestamp", DateUtil.nowTimestampToDouble());
            pushData.put("type", ConstKafka.ACCOUNT_MESSAGE);
            pushData.put("message", messageJson);
            for (String openid : device.getUserMap().keySet()) {
                kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_3, openid, pushData);
            }
        }
//        if (vendorCode != null) {
//            //应该调用外部接口
//            int msgType = 301; // SOS告警
//            openInterfaceService.pushAlarmCallOutInterface(device.getId(), vendorCode, msgType, null, null);
//        }
        return res;
    }

    private Document pushLossDevMsg(Device device, JSONObject message, DeviceLocation newLoc) {
        String deviceName = deviceService.getDeviceName(device);

//        String msg = String.format("【%s】挂失的设备已上线，查看位置", deviceName);
        String msg = MessageUtils.getLocale(device.getLocale(), "API.pushTitle.lossDev", deviceName);
        String msgContent = msg;
        Document res = setNewloctionFunc(device, newLoc, "", message);
        Double lon = Double.valueOf(newLoc.getLon());
        Double lat = Double.valueOf(newLoc.getLat());
        DeviceMessage deviceMessage = new DeviceMessage();
        deviceMessage.setImei(device.getId());
        deviceMessage.setType("lossDev");
        deviceMessage.setName(deviceName);
        deviceMessage.setLat(lat);
        deviceMessage.setLon(lon);
        deviceMessage.setLoc_type(newLoc.getType());
        deviceMessage.setMsgTitle(msg);
        deviceMessage.setMsgContent(msgContent);
        deviceMessageService.save(deviceMessage);

        if (CollectionUtil.isNotEmpty(device.getUserMap())) {
            //发送设置推送
            JSONObject pushData = new JSONObject();
            JSONObject messageJson = new JSONObject();
            messageJson.put("imei", device.getId());
            messageJson.put("type", "lossDev");
            messageJson.put("name", deviceName);
            messageJson.put("lon", lon);
            messageJson.put("lat", lat);
            messageJson.put("loc_type", newLoc.getType());
            messageJson.put("msgTitle", msg);
            messageJson.put("msgContent", msgContent);
            messageJson.put("timestamp", DateUtil.nowTimestampToDouble());
            pushData.put("type", ConstKafka.ACCOUNT_MESSAGE);
            pushData.put("message", messageJson);
            for (String openid : device.getUserMap().keySet()) {
                kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_3, openid, pushData);
            }
        }
        return res;
    }

    /**
     * 是否进出 安全围栏 判断
     *
     * @param device
     * @param message
     * @param newLoc
     * @return
     */
    private Document checkRail(Device device, JSONObject message, DeviceLocation newLoc) {
        Double lon = Double.valueOf(newLoc.getLon());
        Double lat = Double.valueOf(newLoc.getLat());
        Document res = null;
        //是否进出 安全围栏 判断
        if (CollectionUtil.isNotEmpty(device.getRailMap())) {
            //进出安全栏行为动作, 根据安全围栏判断 是否生成轨迹点并推送消息
            boolean flag = false;
            int day = DateUtil.getWeekOfDate(new Date());
            //进出安全栏行为动作, 根据isAlreadyLoc 判断是否创建轨迹点, 然后根据安全围栏判断 是否推送消息
            String deviceName = deviceService.getDeviceName(device);
            for (Map.Entry<String, Map<String, Object>> entry : device.getRailMap().entrySet()) {
                JSONObject railOB = new JSONObject(entry.getValue());

                if (!StringUtil.isNumeric(entry.getValue().get("lon")) ||
                        !StringUtil.isNumeric(entry.getValue().get("lat"))) {
                    log.error("device.getRailMap() lon or lat is exception, imei={}", device.getId());
                    continue;
                }

                //安全围栏的 起始/结束 时间 开启的星期天数
                String starttimeStr = railOB.getString("starttime");
                String endedtimeStr = railOB.getString("endedtime");
                String railWeek = railOB.getString("week");
                //安全围栏的状态
                Integer status = railOB.getInteger("status");
                //是否执行安全围栏判断
                boolean railCheck = false;
                if (Const.INT_1.equals(status)) {
                    if (StringUtil.isNotBlank(railWeek)) {
                        if (railWeek.length() > day - 1 && "1".equals(String.valueOf(railWeek.charAt(day - 1))) ||
                                "0000000".equals(railWeek)) {
                            if (StringUtil.isNotBlank(starttimeStr) && StringUtil.isNotBlank(endedtimeStr)) {
                                int starttime = 0;
                                int endedtime = 0;
                                starttimeStr = starttimeStr.replace(":", "");
                                endedtimeStr = endedtimeStr.replace(":", "");
                                try {
                                    starttime = Integer.parseInt(starttimeStr);
                                    endedtime = Integer.parseInt(endedtimeStr);
                                } catch (Exception error) {
                                    //有可能有 java.lang.NumberFormatException: For input string: "上午730"

                                    if (starttimeStr.contains("午")) {
                                        String tempStr = starttimeStr.split("午")[0];
                                        int tempNum = 0;
                                        if ("下".equalsIgnoreCase(tempStr)) {
                                            tempNum = 1200;
                                        }
                                        starttime = Integer.parseInt(starttimeStr.split("午")[1]) + tempNum;
                                    }
                                    if (endedtimeStr.contains("午")) {
                                        String tempStr = endedtimeStr.split("午")[0];
                                        int tempNum = 0;
                                        if ("下".equalsIgnoreCase(tempStr)) {
                                            tempNum = 1200;
                                        }
                                        endedtime = Integer.parseInt(endedtimeStr.split("午")[1]) + tempNum;
                                    }

                                }
                                if (DateUtil.getHourAndMinute() >= starttime && DateUtil.getHourAndMinute() <= endedtime) {
                                    //安全围栏生效
                                    railCheck = true;
                                }
                            }
                        }
                    }
                }

                if (!railCheck) {
                    continue;
                }

                String action = "";
                if (StringUtil.isNotBlank(entry.getValue().get("action"))) {
                    action = entry.getValue().get("action").toString();
                }
                double lastlon = StringUtil.isNumeric(entry.getValue().get("lon")) ? Double.parseDouble(entry.getValue().get("lon").toString()) : 0;
                double lastlat = StringUtil.isNumeric(entry.getValue().get("lat")) ? Double.parseDouble(entry.getValue().get("lat").toString()) : 0;
                double distance = PositionUtil.getDistance(lastlon, lastlat, lon, lat);

                //半径
                double radius = StringUtil.isNumeric(entry.getValue().get("radius")) ? Double.parseDouble(entry.getValue().get("radius").toString()) : 0;
                if (action.length() == 0) {
                    //新的安全围栏 第一次进行判断, 给个标识, 但不触发轨迹点 和 推送消息
                    if (distance > radius) {
                        entry.getValue().put("action", "leave");
                    } else {
                        entry.getValue().put("action", "enter");
                    }
                    flag = true;
                } else if ("leave".equals(action)) {
                    //不 在这个 区域内 需要判断 是否 进入了 这个区域
                    if (distance < radius) {
                        entry.getValue().put("action", "enter");
                        res = setNewloctionFunc(device, newLoc, "", message);
                        //进入电子围栏，发送进入电子围栏的通知
                        pushRailMsg(device, "enter", entry.getKey(), entry.getValue().get("name").toString(), railOB.getString("vendorCode"));
                        flag = true;
                    }
                } else if ("enter".equals(action)) {
                    //不 在这个 区域内 需要判断 是否 离开了 这个区域
                    if (distance > radius) {
                        entry.getValue().put("action", "leave");
                        res = setNewloctionFunc(device, newLoc, "", message);
                        //出电子围栏，发送出电子围栏的通知
                        pushRailMsg(device, "leave", entry.getKey(), entry.getValue().get("name").toString(), railOB.getString("vendorCode"));
                        flag = true;
                    }
                }
            }
            //遍历完且更改的 再 更新 安全区域 数据
            if (flag) {
                deviceService.updateById(device.getId(), new Update().set("r", device.getRailMap()));
            }
        }
        return res;
    }

    /**
     * 推送安全围栏消息
     *
     * @param device
     * @param action
     * @param railId
     * @param railName
     * @param vendorCode //对外开放接口设置安全围栏厂商编码 (说明是哪个厂商) 为null 则不发
     */
    private void pushRailMsg(Device device, String action, String railId, String railName, String vendorCode) {
        String deviceName = deviceService.getDeviceName(device);
//        String msg = String.format("【%s】离开安全区域 %s", deviceName, railName);
        String msg = MessageUtils.getLocale(device.getLocale(), "API.pushTitle.leaveRail", deviceName, railName);
        if (action.equals("enter")) {
//            msg = String.format("【%s】进入安全区域 %s", deviceName, railName);
            msg = MessageUtils.getLocale(device.getLocale(), "API.pushTitle.enterRail", deviceName, railName);
        }
        String msgContent = msg;
        DeviceMessage deviceMessage = new DeviceMessage();
        deviceMessage.setImei(device.getId());
        deviceMessage.setType("rail");
        deviceMessage.setRailID(railId);
        deviceMessage.setRailName(String.valueOf(railName));
        deviceMessage.setAction(action);
        deviceMessage.setMsgTitle(msg);
        deviceMessage.setMsgContent(msgContent);
        deviceMessageService.save(deviceMessage);

        if (CollectionUtil.isNotEmpty(device.getUserMap())) {
            //发送设置推送
            JSONObject pushData = new JSONObject();
            JSONObject messageJson = new JSONObject();
            messageJson.put("imei", device.getId());
            messageJson.put("type", "rail");
            messageJson.put("name", deviceName);
            messageJson.put("railID", railId);
            messageJson.put("railName", String.valueOf(railName));
            messageJson.put("action", action);
            messageJson.put("msgTitle", msg);
            messageJson.put("msgContent", msgContent);
            messageJson.put("timestamp", DateUtil.nowTimestampToDouble());
            pushData.put("type", ConstKafka.ACCOUNT_MESSAGE);
            pushData.put("message", messageJson);
            for (String openid : device.getUserMap().keySet()) {
                kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_3, openid, pushData);
                 /*Account account = accountService.findById(openid);
                //发送短信通知
                if(ObjectUtils.isNotEmpty(account) && StringUtils.isNotEmpty(account.getPhone())) {
                    //查询设备所绑定的用户有没有购买短信服务
                    UserSmsNotify userSmsNotify = userSmsNotifyService.queryEffeftUserSmsNotifyByOpenId(openid);
                    if("enter".equals(action)) {
                        //发送缓存通知
                        sendSmsNotify(openid,device.getId(),account.getPhone(),4,String.valueOf(1),
                                Constant.ENTER_FENCE_SMS_NOFIGY_POSTFIX,Constant.ENTER_FENCE_SMS_NOFIGY_TEMPLATE_TYPE,
                                Constant.FENCE_SMS_NOFIGY_TEMPLATE_VARIABLE_NAME,userSmsNotify,
                                Integer.valueOf(1).equals(userSmsNotify.getIsOpenFenceSmsNotify()));
                    } else if("leave".equals(action)) {
                        //发送缓存通知
                        sendSmsNotify(openid,device.getId(),account.getPhone(),4,String.valueOf(1),
                                Constant.LEAVE_FENCE_SMS_NOFIGY_POSTFIX,Constant.LEAVE_FENCE_SMS_NOFIGY_TEMPLATE_TYPE,
                                Constant.FENCE_SMS_NOFIGY_TEMPLATE_VARIABLE_NAME,userSmsNotify,
                                Integer.valueOf(1).equals(userSmsNotify.getIsOpenFenceSmsNotify()));
                    }
                }*/
            }
        }
        if (vendorCode != null) {
            //应该调用外部接口
            int msgType = 201; // 进围栏
            if (action.equalsIgnoreCase("leave")) {
                msgType = 200; // 出围栏
            }
            openInterfaceService.pushAlarmCallOutInterface(device.getId(), vendorCode, msgType, railId, railName);
        }
    }


    /**
     * @param [openid, imei, phone, cacheHour, cacheValue, cacheKeyPostfix, notifyTemplate, notifyTemplateVersionName]
     * @return void
     * @description 发送短信通知
     * @author cwj
     * @date @time 2025/3/30 14:48
     **/
    public void sendSmsNotify(String openid, String imei, String phone, Integer cacheHour, String cacheValue, String cacheKeyPostfix,
                              Integer notifyTemplate, String notifyTemplateVersionName, UserSmsNotify userSmsNotify, boolean isOpenServer) {
        if (ObjectUtils.isNotEmpty(userSmsNotify) && userSmsNotify.getSmsSurplusTotal() > 0 && isOpenServer) {
            //再检查几个小时内有没有通知过，定义4个小时内同一个IMEI的对同一个用户只通知一次
            String sendNotifyTimeKey = imei + "-" + openid + cacheKeyPostfix;
            String sendNotifyTime = redisFilterService.get(sendNotifyTimeKey);
            if (StringUtils.isEmpty(sendNotifyTime)) {
                //给用户手机发通知短信
                sendSmsNotifyToUserPhone(imei, phone, cacheHour, cacheValue, notifyTemplate, notifyTemplateVersionName, userSmsNotify, sendNotifyTimeKey);
            } else {
                int sendSmsNotifyTime = Integer.parseInt(sendNotifyTime);
                if (sendSmsNotifyTime <= 0) {
                    //给用户手机发通知短信
                    sendSmsNotifyToUserPhone(imei, phone, cacheHour, cacheValue, notifyTemplate, notifyTemplateVersionName, userSmsNotify, sendNotifyTimeKey);
                }
            }
        }
    }

    /**
     * @param [imei, phone, cacheHour, cacheValue, notifyTemplate, notifyTemplateVersionName, userSmsNotify, sendNotifyTimeKey]
     * @return void
     * @description 给用户手机发送发送通知短信
     * @author cwj
     * @date @time 2025/3/30 15:58
     **/
    private void sendSmsNotifyToUserPhone(String imei, String phone, Integer cacheHour, String cacheValue, Integer notifyTemplate, String notifyTemplateVersionName, UserSmsNotify userSmsNotify, String sendNotifyTimeKey) {
        //直接发送短信通知
        smsService.sendNotify(phone, notifyTemplate, imei, notifyTemplateVersionName);
        //用户购买的短信服务的短信条数减一
        userSmsNotifyService.updateUserSmsSurplusTotal(userSmsNotify.getId(), -1);
        //设置发送过的缓存信息
        redisFilterService.setex(sendNotifyTimeKey, cacheHour * 60 * 60, cacheValue);
    }

    /**
     * 设备 获取可聊天的群组列表
     * 排序 首字母
     *
     * @param device
     * @param message
     * @time: 2021-10-28 18:48
     * @author: Weichao
     * @version 1.0
     */
    private void type_11(Device device, JSONObject message) {
        JSONObject kafkaData = new JSONObject();
        if (message.containsKey("ident")) {
            kafkaData.put("ident", message.get("ident"));
        }

        JSONArray groupList = new JSONArray();
        if (device.getChatGroupMap() != null) {
            //获取设备群聊
            for (String dG :
                    device.getChatGroupMap().keySet()) {
                JSONObject tempG = new JSONObject();
                tempG.put("groupId", dG);
                tempG.put("groupType", 0);
                tempG.put("groupName", "家庭群聊");
                tempG.put("groupImage", "");
                tempG.put("canVideo", 0);
                groupList.add(tempG);
            }
        }

        //单聊
        if (device.getUserMap() != null) {
            JSONArray signGArr = new JSONArray();
            for (String openid : device.getUserMap().keySet()) {
                JSONObject tempG = new JSONObject();
                // 单聊 的群组Id 创建的规则:
                // "用户ID" + & + "imei"
                tempG.put("groupId", openid + "&" + device.getId());
                tempG.put("groupType", 1);
                Map<String, Object> u_User = device.getUserMap().get(openid);
                tempG.put("relationship_image_id", u_User.getOrDefault("relationship_image_id", 0));
                tempG.put("groupName", u_User.getOrDefault("relationship", "未知"));
                tempG.put("groupImage", "");
                tempG.put("callOpenid", openid);
                tempG.put("canVideo", 1); //单聊的用户都是可视频的
                signGArr.add(tempG);

            }
            groupList.addAll(CommonUtil.jsonArraySort(signGArr, "groupName", false));
        }

        // 碰碰交友的
        PpChatGroup ppChatGroup = ppChatGroupService.getByImei(device.getId());
        if (ppChatGroup != null) {
            /*
            {
            "imei": "123456789000002",	<str>//imei
            "time": 时间戳,   <long>// 单位为秒的 时间戳 成为朋友的时间
            "name": "小明",	<str>//昵称,
            "phone": "13400000000",  <str>
            "image": "http://XXX/XXX/XXX.jpg"  <str>//完整的URL 图片链接
            "canVideo"*: 1, <int>   // 好友间是否能视频
            }
            */
            JSONArray friendsArr = new JSONArray();
            for (JSONObject item : ppChatGroupService.getFriends(ppChatGroup)) {
                JSONObject tempFriend = new JSONObject();
                tempFriend.put("groupId", item.getString("imei"));
                tempFriend.put("groupType", 2);
                tempFriend.put("groupName", item.getString("name"));
                tempFriend.put("groupImage", item.getString("image"));
                tempFriend.put("canVideo", item.getIntValue("canVideo"));
                friendsArr.add(tempFriend);
            }
            if (friendsArr.size() > 0) {
                groupList.addAll(CommonUtil.jsonArraySort(friendsArr, "groupName", false));
                log.info("获取到的增加 碰碰交友的 可聊天对象:openidList:{}", groupList.toString());
            }
        }

        kafkaData.put("groupList", groupList);


        kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_11, kafkaData, null);
    }

    /**
     * 国内的天气预报
     *
     * @param device
     * @param message
     * @time: 2021/1/26 9:59
     * @author: Rock
     * @version 1.0
     */
    private void type_upWeather(Device device, JSONObject message) {
        log.info("天气预报处理，设备IMEI为：{}，设备上报的来的数据为：{}", device.getId(), message);
        // 取weather天气预报 ，回应给设备, 由于TCP 已有做缓存访问, 所以这边只要有请求就访问
        JSONObject kafkaData = new JSONObject();
        kafkaData.put("type", ConstKafka.DEVICE_TYPE_DownWeather);
        kafkaData.put("imei", message.getString("imei"));
        kafkaData.put("vender", message.getString("vender"));
        kafkaData.put("ident", message.get("ident"));
        kafkaData.put("isCDMA", message.get("baseStationType"));
        kafkaData.put("errorCode", 0);
        //赛诺目前固定是 获取实时天气
        String extensions = "base";
       /* if(gdBdConfig.getIsBdOrGd() == 0) {
            extensions = "now";
        }*/

        //如果最近的定位点是 GPS(默认没有进行adcode 进行解析的,判断一下,确认)
        if (device.getLoc() != null) {
            JSONObject devLoc = new JSONObject(device.getLoc());
            /*
            "loc" : {
                    "_id" : "2c9d73b28ded8192018ded81927b0000",
                    "lon" : "113.457616",
                    "lat" : "23.190847",
                    "radius" : "0",
                    "T" : "GPS",
                    "useBgAdd" : 0,
                    "t" : 1709086704,
                    "origin_data" : {
                            "feedback" : 1,
                            "baseStationType" : 0,
                            "ident" : 5957670,
                            "kafkaModelType" : 0,
                            "imei" : "123456789000008",
                            "locationType" : "ordinary",
                            "time" : 1709086704,
                            "gps" : {
                                    "lon" : "113.457616",
                                    "lat" : "23.190847"
                            },
                            "vender" : "20000",
                            "type" : "location",
                            "location_time" : 1709086704
                    },
                    "expiryAt" : ISODate("2024-03-07T07:59:59.999Z")
            }
            */
            if (devLoc.getString("T").equals("GPS") && devLoc.getString("adcode") == null) {
                //是 GPS 且 没有 adcode
                //解析一下 adcode 并保存, 同时保存一下 address (addr) 和 adcode
                JSONObject gpsRes = toGPSparsing(listDataProperties, devLoc.getJSONObject("origin_data").getJSONObject("gps"), device.getId(), 460, gdBdConfig.getIsBdOrGd());
                String addr = gpsRes.getString("formatted_address");
                JSONObject addressComponent = gpsRes.getJSONObject("addressComponent");
                Update update = new Update();
                if (addressComponent != null) {
                    String adcode = addressComponent.getString("adcode");
                    update.set("adcode", adcode);
                    RedisClusterService.getJedisCluster().hset(device.getId(), ConstCacheField.WEATHER_ADCODE, adcode);
                    String city = addressComponent.getString("city");
                    update.set("city", city);
                    devLoc.put("adcode", adcode);
                    devLoc.put("city", city);
                }
                update.set("addr", addr);
                devLoc.put("addr", addr);
                //更新轨迹点
                deviceLocationService.updateById(devLoc.getString("_id"), update);
                //
                device.setLoc(devLoc);
                deviceService.save(device);
            }
        }
        //取得此 设备的  adcode
        String adcode = RedisClusterService.getJedisCluster().hget(device.getId(), ConstCacheField.WEATHER_ADCODE);
        log.info("天气预报城市编码为：{}", adcode);
        if (StringUtil.isNotBlank(adcode)) {
            //调用百度或高德的天气查询接口，查询天气信息并组装数据到kafkaData中
            queryWeatherAndSendMsg(device.getId(), adcode, extensions, kafkaData);
            kafkaData.put("adcode", adcode);
            log.info("发送kafka数据为：{}", kafkaData);
            kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_DownWeather, kafkaData, null);
            return;
        }
        //没有adcode 那就查一下有无缓存定位数据
//        kafkaData.put("weather", "未知,未知,0度,无风,0级,2000-01-01 00:00:00更新");
        String jsonStr = redisFilterService.get(device.getId() + CacheEnum.LAST_LBS_DATA.getKey());
        log.info("天气预报城市编码为：{}", adcode);
        if (StringUtil.isNotBlank(jsonStr)) {
            JSONObject result = JSON.parseObject(jsonStr).getJSONObject("result");
            log.info("取出缓存的数据为:{}", result);
            if (result != null && result.size() > 0) {
                adcode = result.getString("adcode");
                if (StringUtil.isNotBlank(adcode) && StringUtil.isNotBlank(result.get("desc"))) {
                    RedisClusterService.getJedisCluster().hset(device.getId(), ConstCacheField.WEATHER_ADCODE, adcode);
                    //调用百度或高德的天气查询接口，查询天气信息并组装数据到kafkaData中
                    queryWeatherAndSendMsg(device.getId(), adcode, extensions, kafkaData);
                    kafkaData.put("adcode", adcode);
                    log.info("发送kafka数据为：{}", kafkaData);
                    kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_DownWeather, kafkaData, null);
                    return;
                }
            }
        }
        //设备还没有上传过定位
        kafkaData.put("errorCode", 10000);
        kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_DownWeather, kafkaData, null);
    }

    /**
     * 国外的天气预报
     *
     * @param device
     * @param message
     * @time: 2/14/22 4:11 PM
     * @author: Weichao
     * @version 1.0
     */
    private void type_19(Device device, JSONObject message) {
        /* 海外天气预报的逻辑说明
           第一次获取天气时:
               没有 locationid 那么使用定位的经纬度去查询具体的城市获取到对应的 城市信息和 当前经纬度的locationid,得到的信息依据 "hf_locid:imei" 为标识key 缓存在天气redis 中 (hashMap形式存储 城市信息);
               然后根据locationid信息获取到天气预报的信息, 依据 "hf:locationid:extensions" 为标识key(分实时和逐天) 缓存在天气redis 中 (hashMap形式),以便下次做缓存处理;
               然后这次获取到的天气预报信息返回给设备.

           第二次获取天气时:(此步应该在TCP进行操作)
               检查"hf_locid:imei" 标识是否缓存了 locationid和城市信息, 有缓存的话,提取缓存的locationid;
               然后依据 "hf:locationid:extensions" 为标识key 在天气redis 中查询缓存, 查到即返回. 否则上报API 再次查询天气预报

           其中:
            当"海外设备"的位置发生更改时 (在设备的定位信息缓存保存的方法里, 应该移除 天气redis中的 "hf_locid:imei" 标识的缓存, 使缓存流程重置)
        */
        // 取weather天气预报 ，回应给设备, 由于TCP 已有做缓存访问, 所以这边只要有请求就访问
        JSONObject kafkaData = new JSONObject();
        kafkaData.put("ident", message.get("ident"));
        kafkaData.put("errorCode", 0);
        /*
        气象类型
        extensions:
		可选值：base/all
		base:返回实况天气 (默认)
		all:返回 (3天)预报天气
        */
        String extensions = message.getString("extensions");
        Integer mcc = message.getInteger("mcc");
        String language = device.getLanguage();

        //查一下有无缓存定位数据, 取定位数据去进行和风请求获取到对应的 locationid
        String jsonStr = redisFilterService.get(device.getId() + CacheEnum.LAST_LBS_DATA.getKey());
        if (StringUtil.isNotBlank(jsonStr)) {
            JSONObject result = JSON.parseObject(jsonStr).getJSONObject("result");
            /*
            "result": {
                    "location": "113.9391249,22.5684901",
                    "type": "4",
                    "radius": "550"
                }
            */
            String location = result.getString("location");
            queryWeatherAndSendMsg_Overseas(device.getId(), language, location, null, extensions, kafkaData);
            kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_19, kafkaData, null);
            return;
        }

        //设备还没有上传过定位
        kafkaData.put("errorCode", 10000);
        kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_19, kafkaData, null);
    }


    /**
     * 上传电量
     *
     * @param device
     * @param message
     * @time: 2021/1/23 15:29
     * @author: Rock
     * @version 1.0
     */
    private void type_battery(Device device, JSONObject message) {
        if (!StringUtil.isNumeric(message.get("battery"))) {
            log.error("type_battery battery is null. message={}", message);
            return;
        }
        Update updateDevice = new Update();
        Integer battery = message.getInteger("battery");
        updateDevice.set("battery", battery);
        if (message.containsKey("max_level")) {
            updateDevice.set("max_level", message.get("max_level"));
        }
        if (message.containsKey("curr_level")) {
            updateDevice.set("curr_level", message.get("curr_level"));
        }

        String locUpdateType = message.getString("locationType");
        if (locUpdateType != null) {
            //定位过来的，确认一下电量是否不足15%
            if ("lowPowerAlarm".equals(locUpdateType) && battery < 15) {
                pushLowBatteryAlarm(device, battery);
//            //判断预留省电的
//            if (electricityStatus == 2) {
//                Integer powerModel = 1;
//                if (!powerModel.equals(device.getPowerModel())) {
//                    deviceService.updateById(device.getId(), new Update().set("powerModel", powerModel));
//                    pushPowerModelStatus(device, powerModel);
//                } else {
//                    log.info("imei: {} 预留电量省电模式 设置相同 powerModel: {} ,不进行触发", device.getId(), powerModel);
//                }
//            }else{
//                //正常态
//                //判断是否有预留省电模式开启,如果有的话,关闭
//                Integer powerModel = device.getPowerModel();
//                if (powerModel != null && powerModel == 1) {
//                    log.info("imei={} 有预留省电模式开启,电量>15 复位为关闭", device.getId());
//                    deviceService.updateById(device.getId(), new Update().set("powerModel", 0));
//                    pushPowerModelStatus(device, 0);
//                }
//            }
            }
        } else {
            //百合的手表 使用手表自己上报低电报警 ,不由服务器判断, 赛诺的需要
            if (battery < 15) {
                pushLowBatteryAlarm(device, battery);
            }
//            if (message.getString("vender") != null || message.getString("vender").equals("20000")) {

//            }
        }

        //更新设备操作
        deviceService.updateById(device.getId(), updateDevice);
//        String dmpAccountId = device.getDmpAccountId();
//        if (dmpAccountId != null) {
//            // 要上传数据到 联通DMP 平台的主题
//            log.info("imei={} 要上传数据到 联通DMP 平台: dmpAccountId: ", device.getId(), dmpAccountId);
//            // kafka.producer.topic-name-dmpDevice=jianyou_flp_DMP_Test
//            JSONObject tempValue = new JSONObject();
//            tempValue.put("dmpAccountId", dmpAccountId);
//            tempValue.put("type", "battery");
//            tempValue.put("battery", "" + battery);
//            kafkaService.sendDMPDeviceWithMsg(device.getId(), tempValue);
//        }
    }

    /**
     * @param [device, message]
     * @return void
     * @description 处理上报来的电量、步数及翻滚次数
     * @author cwj
     * @date @time 2025/5/26 17:06
     **/
    public void type_battery_step_rollNum(Device device, JSONObject message) {
        Update updateDevice = new Update();
        if (StringUtil.isNumeric(message.get("battery"))) {
            Integer battery = message.getInteger("battery");
            updateDevice.set("battery", battery);
            if (battery < 15) {
                pushLowBatteryAlarm(device, battery);
            }
        }
        if (StringUtil.isNumeric(message.get("stepsNum"))) {
            int step = Integer.parseInt(message.get("stepsNum").toString());
            //距离
            double distance = Double.parseDouble(String.format("%.3f", step * 0.6 / 1000d));
            //热量
            double calorie = Double.parseDouble(String.format("%.3f", step * 0.025d));
            updateDevice.set("step", step);
            updateDevice.set("distance", distance);
            updateDevice.set("calorie", calorie);
            String nowDate = DateUtil.dateToStr(new Date(), DateUtil.FORMAT_4);
            Query query = new Query(Criteria.where("i").is(device.getId()).and("date").is(nowDate));
            DeviceStepDaily deviceStepDaily = deviceStepDailyService.findOne(query);
            if (deviceStepDaily == null) {
                deviceStepDaily = new DeviceStepDaily();
                deviceStepDaily.setImei(device.getId());
                deviceStepDaily.setTarget(Const.DEF_TARGET_STEP);
                deviceStepDaily.setDate(nowDate);
                deviceStepDaily.setNum(step);
                deviceStepDaily.setDistance(distance);
                deviceStepDaily.setCalorie(calorie);
                deviceStepDaily.setTime(System.currentTimeMillis() / 1000);
                deviceStepDaily.setExpiryAt(DateUtil.getSystemExpiryAt());
                deviceStepDailyService.save(deviceStepDaily);
            } else {
                Update updateStep = new Update();
                updateStep.set("num", step);
                updateStep.set("distance", distance);
                updateStep.set("calorie", calorie);
                updateStep.set("updateTime", System.currentTimeMillis() / 1000);
                updateStep.set("expiryAt", DateUtil.getSystemExpiryAt());
                deviceStepDailyService.updateById(new ObjectId(deviceStepDaily.getId()), updateStep);
            }
        }
        //翻滚次数
        if (StringUtil.isNumeric(message.get("rollNum"))) {
            int rollNum = Integer.parseInt(message.get("rollNum").toString());
            updateDevice.set("rollNum", rollNum);
            //保存或更新当前翻滚次数的信息
            rollTimeDataService.saveOrUpdateRollTimeData(device.getId(), rollNum);
        }
        if (StringUtil.isNumeric(message.get("battery")) || StringUtil.isNumeric(message.get("stepsNum")) || StringUtil.isNumeric(message.get("rollNum"))) {
            //更新设备操作
            deviceService.updateById(device.getId(), updateDevice);
        }
    }

    /**
     * 省电模式推送
     *
     * @param device
     * @param powerModel
     */
    private void pushPowerModelStatus(Device device, Integer powerModel) {
        String deviceName = deviceService.getDeviceName(device);
        String msg = MessageUtils.getLocale(device.getLocale(), "API.pushTitle.leaveReservedPowerModeTitlt", deviceName);
        String msgContent = MessageUtils.getLocale(device.getLocale(), "API.pushTitle.leaveReservedPowerModeContent", deviceName);
        if (Const.INT_1.equals(powerModel)) {
            msg = MessageUtils.getLocale(device.getLocale(), "API.pushTitle.enterReservedPowerModeTitlt", deviceName);
            msgContent = MessageUtils.getLocale(device.getLocale(), "API.pushTitle.enterReservedPowerModeContent", deviceName);
        }
        DeviceMessage deviceMessage = new DeviceMessage();
        deviceMessage.setImei(device.getId());
        deviceMessage.setName(deviceName);
        deviceMessage.setType("powerModel");
        deviceMessage.setStatus(powerModel);
        deviceMessage.setMsgTitle(msg);
        deviceMessage.setMsgContent(msgContent);
        deviceMessageService.save(deviceMessage);
        if (CollectionUtil.isNotEmpty(device.getUserMap())) {
            for (String openid : device.getUserMap().keySet()) {
                JSONObject data = new JSONObject();
                JSONObject subJson = new JSONObject();
                data.put("type", ConstKafka.ACCOUNT_MESSAGE);
                data.put("message", subJson);
                subJson.put("imei", device.getId());
                subJson.put("type", "powerModel");
                subJson.put("name", deviceName);
                subJson.put("status", powerModel);
                subJson.put("msgTitle", msg);
                subJson.put("msgContent", msgContent);
                subJson.put("timestamp", System.currentTimeMillis() / 1000);
                kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_3, openid, data);
            }
        }
    }

    /**
     * 低电告警推送
     *
     * @param device
     */
    private void pushLowBatteryAlarm(Device device, Integer battery) {
        //先检查低电告警在半个时内是否已经通知过，如果通知过则无需再发告警通知
        String lowBatteryAlarmKey = device.getId() + Constant.LOW_BATTREY_WARN_NOTIFY_EXISTS_POSTFIX;
        String lowBatteryAlarmHasExists = redisFilterService.get(lowBatteryAlarmKey);
        long currentTime = System.currentTimeMillis() / 1000;
        if (StringUtils.isEmpty(lowBatteryAlarmHasExists)) {
            //发送低电告警
            realPushLowBatteryAlarm(device, battery);
            if (device.getBattery() != null && device.getBattery() > battery) {
                redisFilterService.set(lowBatteryAlarmKey, Long.toString(currentTime));
            }
        } else {
            try {
                //自己比较或使用过期时间
                long beforeTime = Long.parseLong(lowBatteryAlarmHasExists);
                if (currentTime - beforeTime > 1800) {
                    //超过半小时就发再推送一次
                    realPushLowBatteryAlarm(device, battery);
                    if (device.getBattery() != null && device.getBattery() > battery) {
                        redisFilterService.del(lowBatteryAlarmKey);
                        redisFilterService.set(lowBatteryAlarmKey, Long.toString(currentTime));
                    }
                }
            } catch (NumberFormatException e) {
                log.error("数值转换失败，缓存低电告警时间类型不是整数数字,异常信息为{}:", e.getMessage());
            }
        }
    }

    /**
     * @param [device, battery]
     * @return void
     * @description 推送低电告警通知
     * @author cwj
     * @date @time 2025/5/31 10:15
     **/
    private void realPushLowBatteryAlarm(Device device, Integer battery) {
        //低电告警
        String deviceName = deviceService.getDeviceName(device);
        //设备的厂商型号
        String vendorCode = null;
        if (device.getAgent() != null) {
            vendorCode = device.getAgent().toString();
        }
//                String msg = deviceName + "%s 低电量";
//                String msgContent = String.format("【%s】设备电量低,请及时充电", deviceName);
        String msg = MessageUtils.getLocale(device.getLocale(), "API.pushTitle.lowBatteryTitle", deviceName);
        String msgContent = MessageUtils.getLocale(device.getLocale(), "API.pushTitle.lowBatteryContent", deviceName);
        //不和之前的一样 而且 电量不增长的
        if (device.getBattery() != null && device.getBattery() > battery) {
            DeviceMessage deviceMessage = new DeviceMessage();
            deviceMessage.setImei(device.getId());
            deviceMessage.setName(deviceName);
            deviceMessage.setType("lowBattery");
            deviceMessage.setBattery(battery);
            deviceMessage.setMsgTitle(msg);
            deviceMessage.setMsgContent(msgContent);
            deviceMessageService.save(deviceMessage);
            if (CollectionUtil.isNotEmpty(device.getUserMap())) {
                JSONObject data = new JSONObject();
                JSONObject subJson = new JSONObject();
                data.put("type", ConstKafka.ACCOUNT_MESSAGE);
                data.put("message", subJson);
                subJson.put("imei", device.getId());
                subJson.put("type", "lowBattery");
                subJson.put("name", deviceName);
                subJson.put("battery", battery);
                subJson.put("msgTitle", msg);
                subJson.put("msgContent", msgContent);
                subJson.put("timestamp", System.currentTimeMillis() / 1000);
                for (String openid : device.getUserMap().keySet()) {
                    kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_3, openid, data);
                    Account account = accountService.findById(openid);
                    if (account != null && StringUtil.isNotBlank(account.getWxgzIdent())
                            && !Const.INT_0.equals(account.getWxgzIdent())) {
//                                String content = String.format("低电告警通知 \n【%s】设备低电量", deviceName);
                        String content = MessageUtils.getLocale(device.getLocale(), "API.pushTitle.lowBatteryAlarm", deviceName);
                        weixinService.replyWxMsg(Constant.WEIXIN_PUSH_TYPE_1, content, account.getWxgzMasterId(), account.getWxid());
                    }
                    //发送短信通知
               /* if(ObjectUtils.isNotEmpty(account) && StringUtils.isNotEmpty(account.getPhone())) {
                    //查询设备所绑定的用户有没有购买短信服务
                    UserSmsNotify userSmsNotify = userSmsNotifyService.queryEffeftUserSmsNotifyByOpenId(openid);
                    //发送短信通知
                    sendSmsNotify(openid,device.getId(),account.getPhone(),4,String.valueOf(1),
                            Constant.SEND_LOW_POWER_SMS_NOFIGY_POSTFIX,Constant.LOW_POWER_SMS_NOFIGY_TEMPLATE_TYPE,
                            Constant.LOW_POWER_SMS_NOFIGY_TEMPLATE_VARIABLE_NAME,userSmsNotify,
                            Integer.valueOf(1).equals(userSmsNotify.getIsOpenLowPowerSmsNotify()));
                }*/
                }
            } else {
                log.warn("type_21 Device u is null. imei={}", device.getId());
            }
//            if (vendorCode != null) {
//                //应该调用外部接口
//                int msgType = 501; // 低电告警
//                openInterfaceService.pushAlarmCallOutInterface(device.getId(), vendorCode, msgType, null, null);
//            }
        }
    }

    /**
     * 上传计步
     *
     * @param device
     * @param message
     */
    private void type_step(Device device, JSONObject message) {
        //计步
        if (StringUtil.isNumeric(message.get("number"))) {
            int step = Integer.parseInt(message.get("number").toString());
            //距离
            double distance = Double.parseDouble(String.format("%.3f", step * 0.6 / 1000d));
            //热量
            double calorie = Double.parseDouble(String.format("%.3f", step * 0.025d));
            Update updateDevice = new Update();
            updateDevice.set("step", step);
            updateDevice.set("distance", distance);
            updateDevice.set("calorie", calorie);

            String nowDate = DateUtil.dateToStr(new Date(), DateUtil.FORMAT_4);
            Query query = new Query(Criteria.where("i").is(device.getId()).and("date").is(nowDate));
            DeviceStepDaily deviceStepDaily = deviceStepDailyService.findOne(query);
            if (deviceStepDaily == null) {
                deviceStepDaily = new DeviceStepDaily();
                deviceStepDaily.setImei(device.getId());
                deviceStepDaily.setTarget(Const.DEF_TARGET_STEP);
                deviceStepDaily.setDate(nowDate);
                deviceStepDaily.setNum(step);
                deviceStepDaily.setDistance(distance);
                deviceStepDaily.setCalorie(calorie);
                deviceStepDaily.setTime(System.currentTimeMillis() / 1000);
                deviceStepDaily.setExpiryAt(DateUtil.getSystemExpiryAt());
                deviceStepDailyService.save(deviceStepDaily);
            } else {
                Update updateStep = new Update();
                updateStep.set("num", step);
                updateStep.set("distance", distance);
                updateStep.set("calorie", calorie);
                updateStep.set("updateTime", System.currentTimeMillis() / 1000);
                updateStep.set("expiryAt", DateUtil.getSystemExpiryAt());
                deviceStepDailyService.updateById(new ObjectId(deviceStepDaily.getId()), updateStep);
            }
            //更新设备操作
            deviceService.updateById(device.getId(), updateDevice);
        }
    }

    /**
     * 上传翻滚次数
     *
     * @param device
     * @param message
     */
    private void type_rollNum(Device device, JSONObject message) {
        //翻滚次数
        if (StringUtil.isNumeric(message.get("number"))) {
            int rollNum = Integer.parseInt(message.get("number").toString());
            Update updateDevice = new Update();
            updateDevice.set("rollNum", rollNum);
            //更新设备操作
            deviceService.updateById(device.getId(), updateDevice);
            //保存或更新当前翻滚次数的信息
            rollTimeDataService.saveOrUpdateRollTimeData(device.getId(), rollNum);
        }
    }


    /**
     * 恢复设备出厂设置
     *
     * @param device
     * @param message
     * @time: 2021/1/25 17:02
     * @author: Rock
     * @version 1.0
     */
    private void type_deviceReset(Device device, JSONObject message) {
        if (device.getLossDevStatus() == 1) {
            log.info("imei:{} 设备已被挂失,不允许设备自行解绑", device.getId());
            return;
        }
        deviceService.reset(device, null, "device");
    }

    /**
     * 设备端拒绝通话，直接推送给app
     *
     * @param device
     * @param message
     * @time: 2021/1/22 18:06
     * @author: Rock
     * @version 1.0
     */
    private void type_upWatchHangUp(Device device, JSONObject message) {
        String deviceVideoId = message.getString("video_identify");
        String videoId = message.getString("video_id");
        if (StringUtil.isBlank(deviceVideoId)) {
            log.error("video_identify is null. imei={}. message={}", device.getId(), message);
            return;
        }
        DeviceVideo deviceVideo = deviceVideoService.getByVideoId(deviceVideoId);
        if (deviceVideo == null) {
            log.error("DeviceVideo is null. imei={}._id={}", device.getId(), deviceVideoId);
            return;
        }
        //==========================检查是否是群聊======================
        boolean isGroupCall = false;
        String groupId = null;
        ChatGroup chatGroup = null;

        // 检查Redis中的通话状态，判断是否是群聊
        String callStatus = redisVideoService.get(CacheEnum.GROUP_VIDEO.getKey() + device.getId());
        if (StringUtil.isNotBlank(callStatus)) {
            isGroupCall = true;
            groupId = callStatus;
            chatGroup = chatGroupService.findById(groupId);
            //log.info("设备 {} 挂断群聊通话，群组ID: {}", device.getId(), groupId);
        }


        String openid = deviceVideo.getOpenid() == null ? "" : deviceVideo.getOpenid();


        // ========== 处理群聊挂断逻辑 ==========
        if (isGroupCall && chatGroup != null) {
            //  检查是否所有成员都已经结束通话，如果是则清除群组通话状态和最后一个成员通话状态
            // 取消和拒绝全走这里了，应该从消息里拿count,是0的话全部清除，不是0就要走checkAndCleanGroupCallStatus
            Integer count = (Integer) message.get("count");
            JSONObject userData = new JSONObject();
            userData.put("time", DateUtil.nowTimestampToDouble());
            //老版本给时间戳
            userData.put("video_id", deviceVideoId);
            userData.put("newVideoId", videoId);
            userData.put("isGroup", 1);      // 表示群聊
            userData.put("count", count);
            userData.put("call_type", deviceVideo.getCallType());
            userData.put("video_identify", deviceVideo.getVideoId());
            userData.put("type", "reject");
            userData.put("imei", device.getId());
            userData.put("name", deviceService.getDeviceName(device));
            JSONObject pushData = new JSONObject();
            pushData.put("type", ConstKafka.ACCOUNT_MESSAGE);
            if (count == null) {
                //  清除设备的通话状态
                redisVideoService.del(CacheEnum.GROUP_VIDEO.getKey() + device.getId());
                log.info(redisVideoService.get(CacheEnum.GROUP_VIDEO.getKey() + device.getId()));
                log.info("清除设备 {} 的群聊通话状态", device.getId());
                //这是设备拒绝通话,检查群组通话人数，如果小于1，则清除群组通话状态
                Map<String, Object> stringObjectMap = deviceVideoService.checkAndCleanGroupCallStatus(chatGroup, groupId);
                Integer count1 = (Integer) stringObjectMap.get("count");
                if (count1 == 1) {
                    //给用户推拒绝
                    // 最后一个人是用户，拿到用户的openid
                    String openId = (String) stringObjectMap.get("lastMemberID");
                    if (CollectionUtil.isNotEmpty(chatGroup.getMemberMap())) {
                        for (String memberOpenId : chatGroup.getMemberMap().keySet()) {
                            userData.put("openid", memberOpenId);
                            pushData.put("message", userData);
                            kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_3, memberOpenId, pushData);
                            log.info("房间数小于1，推给" + memberOpenId);
                        }
                    }
                    ;
                }
            } else if (count != null && count == 0) {
                //这是设备取消通话
                // 清除群组通话状态和所有成员通话状态
                // 给app推送取消,给设备推拒绝
                //  通知所有群组成员
                if (CollectionUtil.isNotEmpty(chatGroup.getMemberMap())) {
                    for (String memberOpenId : chatGroup.getMemberMap().keySet()) {
                        userData.put("openid", memberOpenId);
                        pushData.put("message", userData);
                        kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_3, memberOpenId, pushData);
                        log.info("群组成员 {} 推送取消通话", memberOpenId);
                    }
                }
                // 给设备推送挂断消息
                JSONObject deviceData = new JSONObject();
                deviceData.put("time", DateUtil.nowTimestampToDouble());
                //给设备的是拼接好的
                deviceData.put("video_id", videoId);
                deviceData.put("isGroup", 1);
                deviceData.put("count", count);
                deviceData.put("call_type", deviceVideo.getCallType());
                deviceData.put("hangupType", 1);  // 给设备的消息类型
                deviceData.put("user_openid", device.getId());
                deviceVideoService.clearGroupCallStatus(chatGroup, groupId);
                kafkaService.emitDeviceDemand(device.getId(),
                        ConstKafka.DEVICE_TYPE_DownAPPHangUp, deviceData, openid);
                log.info("设备 {} 推送取消通话", device.getId());

                //  记录群聊未接来电
                recordGroupMissedCall(device, chatGroup, deviceVideo);

            } else {
                // 给设备加状态，判断设备有没有状态
                String s = redisVideoService.get(CacheEnum.GROUP_VIDEO.getKey() + device.getId());
                if (StringUtil.isBlank(s)) {
                    redisVideoService.setex(CacheEnum.GROUP_VIDEO.getKey() + device.getId(),
                            CacheEnum.GROUP_VIDEO.getExpire(), groupId);
                }
            }


        } else {

            Long res = redisVideoService.mdel(CacheEnum.VIDEO.getKey() + device.getId(), CacheEnum.VIDEO.getKey() + openid);
            if (res == 0) {
                log.error("imei:{} 47指令 移除redis标记失败 Key: {} 和 {}",
                        device.getId(), CacheEnum.VIDEO.getKey() + device.getId(), CacheEnum.VIDEO.getKey() + openid);
            }
            //拿到通话记录

            //如果是 设备对设备的不发送挂断推送到APP(也不需要), 发送挂断50 指令到对应的对端设备(对应的设备的imei号就是 openid)
//        String activeProfile = SpringContextUtil.getActiveProfile();
//        log.info("环境:{} imei:{} deviceVideo.getIsDevToDev:{} ",activeProfile, device.getId(), deviceVideo.getIsDevToDev());
            // ========== 原有的单聊处理逻辑 ==========
            Update updateDevice = new Update();
            updateDevice.unset("newVideo_id");
            deviceService.updateById(device.getId(), updateDevice);
            if (StringUtil.isBlank(deviceVideo.getEndOfType())) {
                //只记录一下第一个结束方, 后面的都忽略，单聊的结束方
                deviceVideo.setEndOfType("device");
                if (deviceVideo.getId().contains("-")) {
                    //兼容旧记录  video_id 作为id 的问题 XXXX-XXX
                    deviceVideoService.updateById(deviceVideo.getId(), new Update().set("endOfType", deviceVideo.getEndOfType()));
                } else {
                    deviceVideoService.updateById(new ObjectId(deviceVideo.getId()), new Update().set("endOfType", deviceVideo.getEndOfType()));
                }
            } else {
                //已经记录有 结束方的,证明已经结束了,那么不进行后续逻辑判断了.(设备是不应该有这个指令上传的)
                log.info("imei:{} 已经记录有 结束方的,证明已经结束了,那么不进行后续逻辑判断了.", device.getId());
                return;
            }


            if (deviceVideo.getIsDevToDev() != null && deviceVideo.getIsDevToDev() == 1) {
//            log.info("环境:{} imei:{} 是设备与设备视频的 挂断 deviceVideo:{} ",activeProfile, device.getId(), deviceVideo.toString());
                JSONObject rejectWithOtherDevData = new JSONObject();
                rejectWithOtherDevData.put("type", ConstKafka.DEVICE_TYPE_DownAPPHangUp);
                rejectWithOtherDevData.put("video_id", deviceVideo.getVideoId());
                rejectWithOtherDevData.put("newVideoId", videoId);
                rejectWithOtherDevData.put("video_identify", deviceVideo.getVideoId());
                rejectWithOtherDevData.put("isGroup", 0);
                rejectWithOtherDevData.put("call_type", deviceVideo.getCallType()
                );
                String otherImei = deviceVideo.getOpenid();
                if (device.getId().equals(otherImei)) {
                    otherImei = deviceVideo.getVideoId().split("-")[0];
                }
                // 设备对设备 还需再清除一下,不然在 A打给B，B未接挂断后，B再给A打的场景下, 会清除少一个,device.getId() 和openid是同一个
                redisVideoService.del(CacheEnum.VIDEO.getKey() + otherImei);
                kafkaService.emitDeviceDemand(otherImei, ConstKafka.DEVICE_TYPE_DownAPPHangUp, rejectWithOtherDevData, otherImei);

            } else {
                JSONObject rejectData = new JSONObject();
                JSONObject subJson = new JSONObject();
                rejectData.put("type", ConstKafka.ACCOUNT_MESSAGE);
                rejectData.put("message", subJson);
                subJson.put("isGroup", 0);
                subJson.put("call_type", deviceVideo.getCallType());
                subJson.put("imei", device.getId());
                subJson.put("type", "reject");
                subJson.put("name", deviceService.getDeviceName(device));
                subJson.put("openid", openid);
                subJson.put("video_id", deviceVideo.getVideoId());
                subJson.put("video_identify", deviceVideo.getVideoId());
                subJson.put("newVideoId", videoId);
//            subJson.put("msgTitle", String.format("%s挂断了通话", deviceService.getDeviceName(device)));
                subJson.put("msgTitle", MessageUtils.getLocale(device.getLocale(), "API.pushTitle.video.reject", deviceService.getDeviceName(device)));
                subJson.put("timestamp", StringUtil.isBlank(message.get("time")) ? System.currentTimeMillis() / 1000 : message.get("time"));
                //推挂断用户的 用户主题
                kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_3, openid, rejectData);
                log.info("================设备 {} 挂断, 推给用户 {}", device.getId(), openid);

                if (deviceVideo.getRequestType() != null
                        && !ConstKafka.DEVICE_TYPE_DownVideoCallWithWatch.equals(deviceVideo.getRequestType())
                        && "device".equals(deviceVideo.getEndOfType())) {
                    //发起指令是奇数就是设备发起的, 状态0 是未接通, 且结束挂断是device 的通话记录
                    //换成imei
                    DeviceLastMissedVideoCall call = deviceLastMissedVideoCallService.findByOpenidAndVideoId(openid, deviceVideo.getVideoId());
                    String devName = deviceService.getDeviceName(device);
                    String startTimeShowStr = null;
                    if (deviceVideo.getStartTime() != null) {
                        startTimeShowStr = DateUtil.stampToDateStr(deviceVideo.getStartTime().longValue() * 1000, DateUtil.FORMAT_1);
                    }
                    if (call == null) {
                        deviceLastMissedVideoCallService.add(openid, device.getId(), deviceVideo.getVideoId(), devName, startTimeShowStr);
                    } else {
                        Update updateCall = new Update();
                        updateCall.set("imei", device.getId());
                        updateCall.set("video_id", deviceVideo.getVideoId());
                        updateCall.set("devName", devName);
                        updateCall.set("startTimeShowStr", startTimeShowStr);
                        updateCall.set("creatTime", System.currentTimeMillis() / 1000);
                        deviceLastMissedVideoCallService.updateById(openid, updateCall);
                    }
                }


            }
        }

    }

    /**
     * 记录群聊未接来电
     *
     * @param device      挂断的设备
     * @param chatGroup   群组
     * @param deviceVideo 视频通话记录
     */
    private void recordGroupMissedCall(Device device, ChatGroup chatGroup, DeviceVideo deviceVideo) {
        // 检查是否满足记录未接来电的条件（与单聊逻辑一致）
        if (deviceVideo.getRequestType() != null
                && !ConstKafka.DEVICE_TYPE_DownVideoCallWithWatch.equals(deviceVideo.getRequestType())
        ) {
            //发起指令是奇数就是设备发起的, 状态0 是未接通, 且结束挂断是device 的通话记录
            String devName = deviceService.getDeviceName(device);
            String startTimeShowStr = null;
            if (deviceVideo.getStartTime() != null) {
                startTimeShowStr = DateUtil.stampToDateStr(deviceVideo.getStartTime().longValue() * 1000, DateUtil.FORMAT_1);
            }

            // 为群组中的每个APP成员记录未接来电
            if (CollectionUtil.isNotEmpty(chatGroup.getMemberMap())) {
                for (String memberOpenId : chatGroup.getMemberMap().keySet()) {
                    try {
                        DeviceLastMissedVideoCall call = deviceLastMissedVideoCallService.findByOpenidAndVideoId(memberOpenId, deviceVideo.getVideoId());
                        if (call == null) {
                            deviceLastMissedVideoCallService.add(memberOpenId, device.getId(),
                                    deviceVideo.getVideoId(), devName, startTimeShowStr);
                            //redisVideoService.del(CacheEnum.GROUP_VIDEO.getKey() + memberOpenId);
                        } else {
                            Update updateCall = new Update();
                            updateCall.set("imei", device.getId());
                            updateCall.set("video_id", deviceVideo.getVideoId());
                            updateCall.set("devName", devName);
                            updateCall.set("startTimeShowStr", startTimeShowStr);
                            updateCall.set("creatTime", System.currentTimeMillis() / 1000);
                            deviceLastMissedVideoCallService.updateById(memberOpenId, updateCall);
                        }

                        log.info("记录群聊未接来电: 成员={}, 设备={}, 群组={}",
                                memberOpenId, device.getId(), chatGroup.getName());
                    } catch (Exception e) {
                        log.error("记录群聊未接来电失败: 成员={}", memberOpenId, e);
                    }
                }
            }
        }


    }

    /**
     * 通知群组成员有设备挂断
     */
    private void notifyGroupMembersAboutHangUp(Device device, ChatGroup chatGroup, String videoId) {
        // 首先计算当前房间剩余人数
        int count = calculateRemainingMembersInCall(chatGroup);

        //拿到通话记录
        DeviceVideo deviceVideo = deviceVideoService.findById(videoId);
        // 2. 通知群组中的APP成员
        if (CollectionUtil.isNotEmpty(chatGroup.getMemberMap())) {
            for (String memberOpenId : chatGroup.getMemberMap().keySet()) {
                try {
                    // 检查该成员是否还在通话中
                    String memberCallStatus = redisVideoService.get(CacheEnum.GROUP_VIDEO.getKey() + memberOpenId);
                    if (StringUtil.isNotBlank(memberCallStatus) &&
                            memberCallStatus.equals(chatGroup.getId())) {

                        JSONObject rejectData = new JSONObject();
                        JSONObject subJson = new JSONObject();
                        rejectData.put("type", ConstKafka.ACCOUNT_MESSAGE);
                        rejectData.put("message", subJson);
                        subJson.put("imei", device.getId());
                        subJson.put("type", "reject");
                        // subJson.put("name", deviceService.getDeviceName(device));
                        subJson.put("openid", memberOpenId);
                        subJson.put("groupId", chatGroup.getId());
                        // subJson.put("groupName", chatGroup.getName());
                        subJson.put("isGroup", "1");
                        if (deviceVideo != null && deviceVideo.getCallType() != null) {
                            subJson.put("call_type", deviceVideo.getCallType());
                        }
                        subJson.put("video_id", videoId);
                        subJson.put("count", count);
                        subJson.put("msgTitle", MessageUtils.getLocale(device.getLocale(),
                                "API.pushTitle.video.reject",
                                deviceService.getDeviceName(device), chatGroup.getName()));
                        subJson.put("timestamp", System.currentTimeMillis() / 1000);

                        kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_3, memberOpenId, rejectData);
                        log.info("通知群组APP成员 {} 设备 {} 已挂断", memberOpenId, device.getId());
                    }
                } catch (Exception e) {
                    log.error("通知群组APP成员 {} 失败", memberOpenId, e);
                }
            }
        }

    }


    /**
     * 计算群组中仍在通话的成员数量
     */
    public int calculateRemainingMembersInCall(ChatGroup chatGroup) {
        int count = 0;
        String groupId = chatGroup.getId();

        // 统计设备成员
        if (CollectionUtil.isNotEmpty(chatGroup.getDeviceMap())) {
            for (String deviceImei : chatGroup.getDeviceMap().keySet()) {
                String deviceCallStatus = redisVideoService.get(CacheEnum.GROUP_VIDEO.getKey() + deviceImei);
                if (StringUtil.isNotBlank(deviceCallStatus) && deviceCallStatus.equals(groupId)) {
                    count++;
                }
            }
        }

        // 统计APP成员
        if (CollectionUtil.isNotEmpty(chatGroup.getMemberMap())) {
            for (String memberOpenId : chatGroup.getMemberMap().keySet()) {
                String memberCallStatus = redisVideoService.get(CacheEnum.GROUP_VIDEO.getKey() + memberOpenId);
                if (StringUtil.isNotBlank(memberCallStatus) && memberCallStatus.equals(groupId)) {
                    count++;
                }
            }
        }

        return count;


    }

    /**
     * 检查是否所有群组成员都已结束通话
     */
    private boolean checkIfAllMembersEndedCall(ChatGroup chatGroup, String groupId) {
        // 检查所有设备成员
        if (CollectionUtil.isNotEmpty(chatGroup.getDeviceMap())) {
            for (String deviceImei : chatGroup.getDeviceMap().keySet()) {
                String deviceCallStatus = redisVideoService.get(CacheEnum.GROUP_VIDEO.getKey() + deviceImei);
                if (StringUtil.isNotBlank(deviceCallStatus) && deviceCallStatus.equals(groupId)) {
                    // 还有设备在通话中
                    return false;
                }
            }
        }

        // 检查所有APP成员
        if (CollectionUtil.isNotEmpty(chatGroup.getMemberMap())) {
            for (String memberOpenId : chatGroup.getMemberMap().keySet()) {
                String memberCallStatus = redisVideoService.get(CacheEnum.GROUP_VIDEO.getKey() + memberOpenId);
                if (StringUtil.isNotBlank(memberCallStatus) && memberCallStatus.equals(groupId)) {
                    // 还有APP成员在通话中
                    return false;
                }
            }
        }

        return true;


    }

    /**
     * 设备通话时长上传
     *
     * @param device
     * @param message
     * @time: 2021/1/23 17:34
     * @author: Rock
     * @version 1.0
     */
    private void type_upVideoCallTime(Device device, JSONObject message) {
        if (StringUtil.isNotBlank(message.get("video_id")) && StringUtil.isNumeric(message.get("duration"))) {
            int waitDuration = 0;
            int duration = new Double(message.get("duration").toString()).intValue();
            if (StringUtil.isNumeric(message.get("wait_duration"))) {
                waitDuration = new Double(message.get("wait_duration").toString()).intValue();
            }
            deviceVideoService.updateDeviceVideo(device.getId(), message.getString("video_id"), duration, waitDuration, "device", null);
        }
    }

    /**
     * 视频语音邀请
     *
     * @param device
     * @param message
     * @time: 2021/1/25 9:34
     * @author: Rock
     * @version 1.0
     */
    private void type_upVideoCallWithAPP(Device device, JSONObject message) {
        //设备选择的是 绑定设备的 用户的openid
        String openid = message.getString("user_openid");
        if (openid == null) {
            openid = message.getString("openid");
        }
        //
        Map<String, Object> tempData = new HashMap<>();
        //给设备
        JSONObject emitDeviceDemandData = new JSONObject();
        //视频通话或者语音通话
        Integer callType = Integer.valueOf(message.getOrDefault("call_type", 0).toString());
        Integer isGroup = Integer.valueOf(message.getString("isGroup"));
        String deviceVideoType = "";
        if (device.getModules().contains(ConstVideo.V0VM)) {
            deviceVideoType = "VM";
        } else if (device.getModules().contains(ConstVideo.V0SW)) {
            deviceVideoType = "SW";
        } else if (device.getModules().contains(ConstVideo.V0JF)) {
            deviceVideoType = "JF";
        }
        int code = deviceVideoService.invitateVideo(
                device,
                message.getString("type"),
                deviceVideoType,
                openid,
                message.get("ident"),
                0, // 设备不能监控APP的
                tempData,
                message,
                callType,
                isGroup,
                emitDeviceDemandData,
                1
        );
        String msg = "未知错误,请稍后重试";
        switch (code) {
            case -2:
                msg = "设备小热,请5分钟后再拨";
                break;
            case -3:
                msg = "视频时间过长，3分钟后再拨打吧！";
                break;
            case -4:
                msg = "对方正忙,请稍后再拨";
                break;
            case -5:
                msg = "无效用户,请刷新视频列表";
                break;
            case 1:
                msg = "设备通话时长不足";
                break;
            case 3:
                msg = "对方正在通话中,请稍后重试...";
                break;
            case 4:
                msg = "视频通话的对象不存在,请重新获取视频列表后再试";
                break;
            case 14:
                msg = "对方无视频功能,请检查拨打视频的流程..";
                break;
            case 15:
                msg = "好友不在线";
                break;
            case 998:
                log.info("设备对设备进行视频拨打成功 imei:{}", device.getId());
                return;
            case 999:
                //成功,赋值一些公共参数, 推送给APP
                log.info("message = {}", message);
                if (tempData.size() > 0) {
                    tempData.put("type", "video");
                    tempData.put("openid", openid);
                    tempData.put("name", deviceService.getDeviceName(device));
                    tempData.put("call_type", callType);
                    tempData.put("isGroup", isGroup);
                    //判断 是 语音通话 还是 视频通话
                    if (callType == 0) {
                        tempData.put("msgTitle", MessageUtils.getLocale(device.getLocale(), "API.pushTitle.video.invitationVideo", deviceService.getDeviceName(device)));
                    } else {
                        tempData.put("msgTitle", MessageUtils.getLocale(device.getLocale(), "API.pushTitle.video.invitationAudio", deviceService.getDeviceName(device)));
                    }

                    if (StringUtil.isNotBlank(message.get("platform"))) {
                        tempData.put("platform", message.get("platform"));
                        deviceService.updateById(device.getId(), new Update().set("platform", message.get("platform")));
                    }
                    // ========== 检查是否是群聊（使用协议中的参数名） ==========
                    if (isGroup == 0) {
                        // 单聊，只推送给指定的openid
                        JSONObject dataPush = new JSONObject();
                        tempData.put("initiator", device.getName());
                        dataPush.put("type", ConstKafka.ACCOUNT_MESSAGE);
                        dataPush.put("message", tempData);
                        kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_3, openid, dataPush);
                    }
//                    JSONObject dataPush = new JSONObject();
//                    dataPush.put("type", ConstKafka.ACCOUNT_MESSAGE);
//                    dataPush.put("message", tempData);
//                    kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_3, openid, dataPush);
//                    if (CollectionUtil.isNotEmpty(device.getChatGroupMap())) {
//
//                        //log.info("dataPush = {}", dataPush);
//                        for (String groupId : device.getChatGroupMap().keySet()) {
//                            kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_1, groupId, dataPush);
//                        }
//                    } else {
//                        log.error("imei: {}表中 device 中 g为空", device.getId());
//                    }
                    return;
                }
            default:
                log.error(" 设备imei={} 发起通话失败,未知错误,请稍后重试. code={}. message={}", device.getId(), code, message);
        }
        if (code != 999) {
            JSONObject dataPush = new JSONObject();
            dataPush.put("message", msg);
            dataPush.put("isGroup", isGroup);
            dataPush.put("call_Type", callType);
            dataPush.put("user_openid", openid);
            kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_DownVideoCallFailInfo, dataPush, null);
        }
    }

    /**
     * 获取手表可视频的用户反馈给手表
     *
     * @param device
     * @param message
     * @time: 2021/1/22 17:06
     * @author: Rock
     * @version 1.0
     */
    private void type_upGetVideoUserList(Device device, JSONObject message) {
        JSONArray openidList = new JSONArray();

        //这个是整改后的获取视频通话联系人的
        if (CollectionUtil.isNotEmpty(device.getCheckedOpenidsMap())) {
            JSONArray canCallUserList = new JSONArray();
            for (Map.Entry<String, Map<String, Object>> entry : device.getCheckedOpenidsMap().entrySet()) {
                if (entry.getValue() != null) {
                    JSONObject itemMap = new JSONObject(entry.getValue());
                    JSONObject newTempMap = new JSONObject();

                    //TODO 这里是做一下补偿,是 Python 切换成Java 的后遗症 (后续要移除此操作) 下面的也是
                    if (!itemMap.containsKey("relationship") || itemMap.getString("relationship") == null) {
                        //调用一下获取通讯录的接口, 兼容处理一下联系人关系等.
                        deviceService.getContact(device.getId());
                        itemMap.put("relationship", "");
                        itemMap.put("relationship_image_id", 9);
                    }
                    newTempMap.put("relationship_image_id", itemMap.getIntValue("relationship_image_id"));
                    newTempMap.put("relationship", itemMap.getString("relationship"));
                    if (itemMap.containsKey("c")) {
                        Object tempOpenid = itemMap.get("c");
                        itemMap.remove("c");
                        newTempMap.put("user_openid", tempOpenid);
                    }
                    if (itemMap.containsKey("phone")) {
                        newTempMap.put("phone", itemMap.get("phone"));
                    }
                    itemMap.remove("g");
                    itemMap.remove("t");
                    newTempMap.put("canVideo", 1);
                    canCallUserList.add(newTempMap);
                }
            }
            //排序
            openidList.addAll(CommonUtil.jsonArraySort(canCallUserList, "relationship", false));
        } else if (CollectionUtil.isNotEmpty(device.getContactMap())) {
            // TODO  这个是临时的,用python的老方法读取 通讯录里的视频联系人
            JSONArray canCallUserList = new JSONArray();
            for (Map.Entry<String, Map<String, Object>> entry : device.getContactMap().entrySet()) {
                if (entry.getValue() != null) {
                    JSONObject itemMap = new JSONObject(entry.getValue());
                    JSONObject newTempMap = new JSONObject();
                    Integer canVideo = itemMap.getInteger("canVideo");
                    if ((canVideo != null && canVideo == 1) || "admin".equals(itemMap.getString("identity"))) {
                        newTempMap.put("canVideo", canVideo);
                        newTempMap.put("relationship_image_id", itemMap.getIntValue("image"));
                        newTempMap.put("relationship", itemMap.getString("name"));
                        if (itemMap.containsKey("c")) {
                            Object tempOpenid = itemMap.get("c");
                            itemMap.remove("c");
                            newTempMap.put("user_openid", tempOpenid);
                        }
                        if (itemMap.containsKey("phone")) {
                            newTempMap.put("phone", itemMap.get("phone"));
                        }
                        canCallUserList.add(newTempMap);
                    }
                    ;
                }
            }
            //排序
            openidList.addAll(CommonUtil.jsonArraySort(canCallUserList, "relationship", false));
        }


//        //碰碰交友
//        PpChatGroup ppChatGroup = ppChatGroupService.getByImei(device.getId());
//        if (ppChatGroup != null) {
//            JSONArray friendsArr = new JSONArray();
//            for (JSONObject item : ppChatGroupService.getFriends(ppChatGroup)) {
//                JSONObject tempFriend = new JSONObject();
//                tempFriend.put("openid", item.getString("imei"));
//                tempFriend.put("isDev", 1);
//                tempFriend.put("relationship", item.getString("name"));
//                tempFriend.put("relationship_image_id", 9);
//                if(item.getIntValue("canVideo") == 1){
//                    friendsArr.add(tempFriend);
//                }
//            }
//
//            openidList.addAll(CommonUtil.jsonArraySort(friendsArr,"relationship", false));
//            log.info("获取到的增加 碰碰交友的 可拨打对象:openidList:{}", openidList.toString());
//        }


        JSONObject data = new JSONObject();
        data.put("ident", message.get("ident"));
        if (message.containsKey("vender")) {
            data.put("vender", message.get("vender"));
        }
        data.put("user_list", openidList);
        kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_DownGetVideoUserList, data, null);

    }

    /**
     * 收到查询话费流量短信内容时处理
     *
     * @param device
     * @param message
     * @time: 2021/1/22 11:54
     * @author: Rock
     * @version 1.0
     */
    private void type_uploadCostFlow(Device device, JSONObject message) {
        DeviceCostFlow deviceCostFlow = new DeviceCostFlow();
        deviceCostFlow.setImei(device.getId());
        deviceCostFlow.setContent(message.getString("content"));
        deviceCostFlowService.save(deviceCostFlow);
        if (CollectionUtil.isNotEmpty(device.getUserMap())) {
            String deviceName = StringUtil.isBlank(device.getName()) ? "宝贝：" + device.getId() : device.getName();
            JSONObject data = new JSONObject();
            JSONObject subJson = new JSONObject();
            data.put("type", ConstKafka.ACCOUNT_MESSAGE);
            data.put("message", subJson);
            subJson.put("imei", device.getId());
            subJson.put("type", "CostFlow");
            subJson.put("content", message.getString("content"));
            subJson.put("msgTitle", MessageUtils.getLocale(device.getLocale(), "API.pushTitle.costFlowTitle", deviceName));
            subJson.put("msgContent", MessageUtils.getLocale(device.getLocale(), "API.pushTitle.costFlowContent", deviceName));
            subJson.put("name", deviceName);
            subJson.put("timestamp", System.currentTimeMillis() / 1000);
            for (String openid : device.getUserMap().keySet()) {
                if (RedisAccountClusterService.getJedisCluster().exists(CacheEnum.INQUIRE.getKey() + device.getId() + ":" + openid)) {
                    kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_3, openid, data);
                }
            }
        } else {
            log.error("type_uploadCostFlow Device u is null. imei={}", device.getId());
        }
    }

    /**
     * 视频信号不好的情况
     *
     * @param device
     * @param message
     * @time: 2021/3/25 11:35
     * @author: Rock
     * @version 1.0
     */
    private void type_63(Device device, JSONObject message) {
        String videoId = message.getString("video_id");
        DeviceVideo deviceVideo = deviceVideoService.getByVideoId(videoId);
        if (deviceVideo == null) {
            log.error("DeviceVideo is null. _id={}", videoId);
            return;
        }
        int videoNetStatus = 0;
        if (StringUtil.isNumeric(message.get("status"))) {
            videoNetStatus = new Double(message.getString("status")).intValue();
        }
        if (deviceVideo.getIsDevToDev() != null && deviceVideo.getIsDevToDev() == 1) {
            // 下发 ConstKafka.DEVICE_TYPE_DownVideoCallNetStatus 指令 到对端设备
            JSONObject videoNetStatusOB = new JSONObject();
            videoNetStatusOB.put("type", ConstKafka.DEVICE_TYPE_DownVideoCallNetStatus);
            videoNetStatusOB.put("status", videoNetStatus);
            videoNetStatusOB.put("video_id", videoId);
            String otherImei = deviceVideo.getOpenid();
            if (device.getId().equals(otherImei)) {
                otherImei = deviceVideo.getVideoId().split("-")[0];
            }
            kafkaService.emitDeviceDemand(otherImei, ConstKafka.DEVICE_TYPE_DownVideoCallNetStatus, videoNetStatusOB, null);
            return;
        }
        String topic = "iot08/user/" + deviceVideo.getOpenid();
        JSONObject data = new JSONObject();
        JSONObject subJson = new JSONObject();
        data.put("type", ConstKafka.ACCOUNT_MESSAGE);
        data.put("openid", deviceVideo.getOpenid());
        data.put("message", subJson);
        subJson.put("imei", device.getId());
        subJson.put("type", " ");
        subJson.put("video_id", videoId);
        subJson.put("openid", deviceVideo.getOpenid());
        subJson.put("videoNetStatus", videoNetStatus);
        kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_4, topic, data);
    }

    /**
     * 碰碰交友上传的 携带在 TCP生成的 验证码code
     *
     * @param device
     * @param message
     * @time: 2022/1/19 11:50
     * @author: WeiChao
     * @version 1.0
     */
    private void type_upMakeFriend(Device device, JSONObject message) {

        //=============验证码建交===================
//        String code = message.getString("code");
//        if (code != null) {
//            //71 的code 在交友的redis中 生成一个 key 为code , value 为 imei 的键值对 (设置失效时间5分钟)
//            redisPPMFAndWeatherService.setex(CacheEnum.PPMF.getKey() + code, CacheEnum.PPMF.getExpire(), device.getId());
//        }
//        return;

        //=============根据距离判定的================

        //直接解析位置然后存进redis
        double lon = 0;
        double lat = 0;
        String locationType = "";
        JSONObject gpsJson = message.getJSONObject("gps");
        if (gpsJson != null && StringUtil.isNotBlank(gpsJson.get("lon")) && StringUtil.isNotBlank(gpsJson.get("lat"))) {
            lon = Double.parseDouble(gpsJson.get("lon").toString());
            lat = Double.parseDouble(gpsJson.get("lat").toString());
            locationType = "GPS";
        } else {
            JSONObject resultJson = new JSONObject();
            executionLocateFuntion(device.getId(), message, device, resultJson);
            locationType = resultJson.getString("locationType");
            JSONObject locationJson = resultJson.getJSONObject("data");
            if (locationJson != null && locationJson.containsKey("result")) {
                JSONObject result = locationJson.getJSONObject("result");
                if (result != null && StringUtil.isNotBlank(result.get("location"))) {
                    String[] locationArr = result.getString("location").split(",");
                    if (locationArr.length >= 2) {
                        if (StringUtil.isNotBlank(locationArr[0])) {
                            lon = Double.parseDouble(locationArr[0]);
                        }
                        if (StringUtil.isNotBlank(locationArr[1])) {
                            lat = Double.parseDouble(locationArr[1]);
                        }
                    } else {
                        log.error("locations is Exception. locationJson={}", locationJson);
                    }
                }
            }
        }
        if (lon == 0 && lat == 0) {
            log.info("imei: {} 解析不到位置, 0,0 不回复指令", device.getId());
            return;
        }

        // 找出所有的 碰碰交友Key列表  PPMF:imei
        Set<String> ppmfKeys = redisPPMFAndWeatherService.keys(CacheEnum.PPMF.getKey() + "*");
        if (ppmfKeys == null) {
            return;
        }
        //log.info("ppmfKeys set = {}", ppmfKeys);
        JSONObject locationData = new JSONObject();
        locationData.put("ident", message.get("ident"));
        locationData.put("lon", lon);
        locationData.put("lat", lat);
        locationData.put("locationType", locationType);
        redisPPMFAndWeatherService.setex(CacheEnum.PPMF.getKey() + device.getId(), CacheEnum.PPMF.getExpire(), locationData.toJSONString());
        PpChatGroup ppChatGroup = ppChatGroupService.getByImei(device.getId());
        if (ppChatGroup == null) {
            ppChatGroup = ppChatGroupService.add(device.getId(), null);
        }
        for (String key : ppmfKeys) {
            String[] keyArr = key.split(":");
            if (keyArr.length < 2 || device.getId().equals(keyArr[1])) {
                continue;
            }
            String otherImei = keyArr[1];
            String value = redisPPMFAndWeatherService.get(CacheEnum.PPMF.getKey() + otherImei);
            if (StringUtil.isBlank(value)) {
                continue;
            }
            JSONObject dataJson = JSON.parseObject(value);
            double distance = PositionUtil.getDistance(lon, lat, lon, lat);
            if (StringUtil.isNotBlank(dataJson.get("lon")) && StringUtil.isNotBlank(dataJson.get("lat"))) {
                distance = PositionUtil.getDistance(lon, lat, Double.parseDouble(dataJson.getString("lon")), Double.parseDouble(dataJson.getString("lat")));
            }
            //限制距离根据 2个的 定位类型进行调整 同是 GPS 和 WIFI 的100米 LBS 的500米
            int limitDistance = Const.LIMIT_DISTANCE_OTHER;
            if ("LBS".equals(locationType) && "LBS".equals(dataJson.get("locationType"))) {
                limitDistance = Const.LIMIT_DISTANCE_LBS;
            }
            if (distance <= limitDistance) {
                //达成交友条件
                PpChatGroup pcg = ppChatGroupService.getByImei(otherImei);
                if (pcg != null) {
                    if (CollectionUtil.containsKey(pcg.getMemberMap(), device.getId())) {
                        //已交过友了，下发失败通知
                        JSONObject data = new JSONObject();
                        data.put("success", -1);
                        data.put("ident", message.get("ident"));
                        data.put("failimei", otherImei);
                        kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_DowmMakeFriend, data, null);
                        data.put("ident", dataJson.get("ident"));
                        data.put("failimei", device.getId());
                        kafkaService.emitDeviceDemand(otherImei, ConstKafka.DEVICE_TYPE_DowmMakeFriend, data, null);

                        data = new JSONObject();
                        data.put("syncfriends", ppChatGroupService.getFriends(ppChatGroup));
                        kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_DownSyncFriend, data, null);
                        data.put("syncfriends", ppChatGroupService.getFriends(pcg));
                        kafkaService.emitDeviceDemand(otherImei, ConstKafka.DEVICE_TYPE_DownSyncFriend, data, null);
                        continue;
                    } else {
                        ppChatGroupService.addMember(pcg, device.getId());
                    }
                } else {
                    ppChatGroupService.add(otherImei, device.getId());
                }
                if (!CollectionUtil.containsKey(ppChatGroup.getMemberMap(), otherImei)) {
                    ppChatGroupService.addMember(ppChatGroup, otherImei);
                }
                //清除掉2个 交友成功的人 交友数据
                redisPPMFAndWeatherService.mdel(CacheEnum.PPMF.getKey() + otherImei, CacheEnum.PPMF.getKey() + device.getId());

                JSONObject dataPush = new JSONObject();
                dataPush.put("syncfriends", ppChatGroupService.getFriends(ppChatGroup));
                kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_DownSyncFriend, dataPush, null);
                dataPush.put("syncfriends", ppChatGroupService.getFriends(pcg));
                kafkaService.emitDeviceDemand(otherImei, ConstKafka.DEVICE_TYPE_DownSyncFriend, dataPush, null);
                dataPush.clear();

                dataPush.put("success", 1);
                dataPush.put("ident", message.get("ident"));
                dataPush.put("successimei", otherImei);
                kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_DowmMakeFriend, dataPush, null);
                dataPush.put("ident", dataJson.get("ident"));
                dataPush.put("successimei", device.getId());
                kafkaService.emitDeviceDemand(otherImei, ConstKafka.DEVICE_TYPE_DowmMakeFriend, dataPush, null);
                log.info("交友成功，符合限制距离！imei = {}. otherImei={}. distance={}", device.getId(), otherImei, distance);
            } else {
                //交友失败
                //log.info("交友失败，不符合限制距离！imei = {}. otherImei={}. distance={}", device.getId(), otherImei, distance);
            }
        }


    }


    /**
     * 碰碰交友 匹配验证 验证码code
     *
     * @param device
     * @param message
     * @time: 2022/1/19 11:50
     * @author: WeiChao
     * @version 1.0
     */
    private void type_77(Device device, JSONObject message) {
        String code = message.getString("code");
        if (code != null) {
            //77 的code 在交友的redis 中进行匹配, 匹配不到就返回失败, 成功, 移除键值对. 建立交友逻辑
            String otherImei = redisPPMFAndWeatherService.get(CacheEnum.PPMF.getKey() + code);
            JSONObject dataPush = new JSONObject();
            dataPush.put("ident", message.get("ident"));
            if (otherImei != null) {
                // 成功
                //查找发77 指令的设备的 交友群组
                PpChatGroup ppChatGroup = ppChatGroupService.getByImei(device.getId());
                if (ppChatGroup == null) {
                    ppChatGroup = ppChatGroupService.add(device.getId(), null);
                }
                //达成交友条件
                PpChatGroup pcg = ppChatGroupService.getByImei(otherImei);
                if (pcg != null) {
                    if (CollectionUtil.containsKey(pcg.getMemberMap(), device.getId())) {
                        //重复交友, 下发通知

                        dataPush.put("success", -1);
//                        kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_77, dataPush);
                        return;
                    } else {
                        ppChatGroupService.addMember(pcg, device.getId());
                    }
                } else {
                    ppChatGroupService.add(otherImei, device.getId());
                }
                if (!CollectionUtil.containsKey(ppChatGroup.getMemberMap(), otherImei)) {
                    ppChatGroupService.addMember(ppChatGroup, otherImei);
                }
                dataPush.put("success", 1);
                redisPPMFAndWeatherService.del(CacheEnum.PPMF.getKey() + code);
            } else {
                // 失败, 找不到验证码
                dataPush.put("success", 0);
            }
//            kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_77, dataPush);

        }
    }

    /**
     * 同步好友列表
     *
     * @param device
     * @param message
     * @time: 2021/1/22 16:47
     * @author: Rock
     * @version 1.0
     */
    private void type_upSyncFriend(Device device, JSONObject message) {
        PpChatGroup ppChatGroup = ppChatGroupService.getByImei(device.getId());
        JSONObject json = new JSONObject();
        if (ppChatGroup != null) {
            json.put("syncfriends", ppChatGroupService.getFriends(ppChatGroup));
        } else {
            json.put("syncfriends", new ArrayList<>());
        }
        kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_DownSyncFriend, json, null);
    }

    /**
     * 删除碰碰交友
     *
     * @param device
     * @param message
     * @time: 2021/1/22 16:40
     * @author: Rock
     * @version 1.0
     */
    private void type_upDelFriend(Device device, JSONObject message) {
        PpChatGroup ppChatGroup = ppChatGroupService.getByImei(device.getId());
        if (ppChatGroup == null || !CollectionUtil.isNotEmpty(ppChatGroup.getMemberMap())) {
            log.error("type_75 ppChatGroup member is null. imei={}. message={}", device.getId(), message);
            return;
        }
        Integer ident = message.getInteger("ident");
        JSONArray targetimeiarr = new JSONArray();
        if (message.containsKey("pp_imei")) {
            targetimeiarr.add(message.getString("pp_imei"));
        }
        if (message.containsKey("pp_imei_list")) {
            targetimeiarr = message.getJSONArray("pp_imei_list");
        }

        if (targetimeiarr != null && targetimeiarr.size() > 0) {
            boolean isDel = false;
            for (int i = 0; i < targetimeiarr.size(); i++) {
                String imei = targetimeiarr.getString(i);
                if (StringUtil.isBlank(imei)) {
                    continue;
                }
                //删除自己的群组中的他人的imei
                CollectionUtil.removeMapKey(ppChatGroup.getMemberMap(), imei);
                //删除他人的群组中的自己的imei
                PpChatGroup cg = ppChatGroupService.getByImei(imei);
                if (cg != null) {
                    if (cg.getMemberMap() == null) {
                        //解除碰碰交友失败
                        log.info("imei:{} 解除碰碰交友失败, targetimeiarr:{}", device.getId(), targetimeiarr);
                        JSONObject json = new JSONObject();
                        json.put("ident", ident);
                        json.put("status", 0);
                        json.put("pp_imei_list", targetimeiarr);
                        kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_DownDelFriend, json, null);
                        return;
                    }
                    CollectionUtil.removeMapKey(cg.getMemberMap(), device.getId());
                    //删除更新
                    UpdateVO updateSub = new UpdateVO();
                    updateSub.setMap("m", cg.getMemberMap());
                    ppChatGroupService.updateById(new ObjectId(cg.getId()), updateSub);
                    //下发给 被解除的碰碰好友的解除好友指令
                    JSONObject downDelFriendJSON = new JSONObject();
                    downDelFriendJSON.put("status", 1);
                    downDelFriendJSON.put("pp_imei", device.getId());
                    kafkaService.emitDeviceDemand(imei, ConstKafka.DEVICE_TYPE_DownDelFriend, downDelFriendJSON, null);

                    //并同步对应被删除的人的好友列表
                    JSONObject json = new JSONObject();
                    json.put("syncfriends", ppChatGroupService.getFriends(cg));
                    kafkaService.emitDeviceDemand(imei, ConstKafka.DEVICE_TYPE_DownSyncFriend, json, null);
                    //真的有删到
                    isDel = true;
                }
            }
            //发送通知
            JSONObject json = new JSONObject();
            json.put("ident", ident);
            if (isDel) {
                //删除成功
                //更新操作
                UpdateVO update = new UpdateVO();
                update.setMap("m", ppChatGroup.getMemberMap());
                ppChatGroupService.updateById(new ObjectId(ppChatGroup.getId()), update);
                JSONObject syncfriendsJSON = new JSONObject();
                syncfriendsJSON.put("syncfriends", ppChatGroupService.getFriends(ppChatGroup));
                kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_DownSyncFriend, syncfriendsJSON, null);


                json.put("status", 1);
                json.put("pp_imei", targetimeiarr.get(0));
            } else {
                //删除失败
                json.put("status", 0);
                json.put("pp_imei", "");
                json.put("pp_imei_failList", new JSONArray());
            }

            kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_DownDelFriend, json, null);
        } else {
            JSONObject json = new JSONObject();
            json.put("status", 0);
            json.put("message", "not find targetimeiarr");
            json.put("ident", ident);
            kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_DownDelFriend, json, null);
        }
    }

    /**
     * 设备上传 自身手机号
     *
     * @param device
     * @param message
     * @time: 2021/1/22 15:49
     * @author: Rock
     * @version 1.0
     */
    private void type_81(Device device, JSONObject message) {
        if (StringUtil.isNotBlank(message.get("newphone"))) {
            Update updateDevice = new Update();
            updateDevice.set("phone", message.get("newphone"));
            deviceService.updateById(device.getId(), updateDevice);
            if (device.getWatchCustomFunction() != null && device.getWatchCustomFunction().contains("PPMF")) {
                PpChatGroup ppChatGroup = ppChatGroupService.getByImei(device.getId());
                if (ppChatGroup != null && CollectionUtil.isNotEmpty(ppChatGroup.getMemberMap())) {
                    JSONObject json = new JSONObject();
                    for (String key : ppChatGroup.getMemberMap().keySet()) {
                        PpChatGroup cg = ppChatGroupService.getByImei(key);
                        if (cg != null) {
                            ppChatGroupService.addMember(cg, device.getId());
//                            json.put("syncfriends", ppChatGroupService.getFriends(cg));
//                            kafkaService.emitDeviceDemand(key, ConstKafka.DEVICE_TYPE_72, json);
                        }
                    }
                }
            }
        } else {
            log.error("type_81 newphone is null. message={}", message);
        }
    }


    /**
     * 设备上传的是否 超额断网 的标识
     *
     * @param device
     * @param message
     * @time: 2021/1/22 11:26
     * @author: Rock
     * @version 1.0
     */
    private void type_95(Device device, JSONObject message) {
        DeviceDataFlow dataFlow = deviceDataFlowService.getById(device.getId());
        Integer dataFlowStatus = StringUtil.isBlank(message.get("status")) ? 0 : Integer.parseInt(message.get("status").toString());
        if (Const.INT_1.equals(dataFlowStatus) || !dataFlowStatus.equals(dataFlow.getStatus() == null ? 0 : dataFlow.getStatus())) {
            Integer nowMonthPlanValue = dataFlow.getPackageValue() == null ? 0 : dataFlow.getPackageValue();

            String monthPlanTitle = "自定义";
            for (Map<String, Object> map : listDataProperties.getFlow()) {
                if (map.get("ident").equals(dataFlow.getMonthPlan())) {
                    nowMonthPlanValue = (Integer) map.get("ident");
                    monthPlanTitle = (String) map.get("title");
                }
            }
            String deviceName = deviceService.getDeviceName(device);
            String msg = MessageUtils.getLocale(device.getLocale(), "API.pushTitle.restoreDataFlow", deviceName);
            String dataFlowType = "restoreDataFlow";
            if (Const.INT_1.equals(dataFlowStatus)) {
                dataFlowType = "excessDataFlow";
                msg = MessageUtils.getLocale(device.getLocale(), "API.pushTitle.excessDataFlow", deviceName);
            }
            DeviceMessage deviceMessage = new DeviceMessage();
            deviceMessage.setImei(device.getId());
            deviceMessage.setName(deviceName);
            deviceMessage.setMonthPlanV(nowMonthPlanValue);
            deviceMessage.setType(dataFlowType);
            deviceMessage.setMsgTitle(msg);
            deviceMessage.setMsgContent(msg);
            deviceMessageService.save(deviceMessage);
            if (CollectionUtil.isNotEmpty(device.getUserMap())) {
                for (String openid : device.getUserMap().keySet()) {
                    JSONObject data = new JSONObject();
                    JSONObject subJson = new JSONObject();
                    data.put("type", ConstKafka.ACCOUNT_MESSAGE);
                    data.put("message", subJson);
                    subJson.put("id", deviceMessage.getId());
                    subJson.put("imei", device.getId());
                    subJson.put("type", dataFlowType);
                    subJson.put("monthPlanV", nowMonthPlanValue);
                    subJson.put("name", deviceName);
                    subJson.put("msgTitle", msg);
                    subJson.put("msgContent", msg);
                    subJson.put("timestamp", System.currentTimeMillis() / 1000);
                    kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_3, openid, data);
                }
            }
        } else {
            if (CollectionUtil.isNotEmpty(device.getUserMap())) {
                for (String openid : device.getUserMap().keySet()) {
                    if (RedisAccountClusterService.getJedisCluster().exists(CacheEnum.DATA_FLOWQ.getKey() + device.getId() + ":" + openid)) {
                        JSONObject data = new JSONObject();
                        JSONObject subJson = new JSONObject();
                        data.put("type", ConstKafka.ACCOUNT_MESSAGE);
                        data.put("message", subJson);
                        subJson.put("imei", device.getId());
                        subJson.put("type", "updateDataFlow");
                        subJson.put("timestamp", System.currentTimeMillis() / 1000);
                        kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_3, openid, data);
                    }
                }
            }
        }
        Update updateDataFlow = new Update();
        double nowDataflow = 0;
        if (StringUtil.isNumeric(message.get("dataFlow"))) {
            nowDataflow = Double.parseDouble(String.format("%.3f", Double.parseDouble(message.get("dataFlow").toString()) / 1024.0d));
        }
        updateDataFlow.set("nowDataflow", nowDataflow);
        updateDataFlow.set("updateTime", System.currentTimeMillis() / 1000);
        updateDataFlow.set("queryTime", System.currentTimeMillis() / 1000);
        updateDataFlow.set("status", dataFlowStatus);
        deviceDataFlowService.updateById(device.getId(), updateDataFlow);
    }

    /**
     * 设备上传健康数据
     *
     * @param device
     * @param message
     * @time: 2021/3/9 16:19
     * @author: Rock
     * @version 1.0
     */
    private void type_101(Device device, JSONObject message) {
        //(心率/血压/血氧/体温/血糖 0 / 1 / 2 / 3 / 4 )
        Integer cmdType = 0;
        switch (message.getString("type")) {
            case "upHeartRate":
                cmdType = 0;
                break;
            case "upBP":
                cmdType = 1;
                break;
            case "upBO":
                cmdType = 2;
                break;
            case "upBodyTemperature":
                cmdType = 3;
                break;
            case "upBS":
                cmdType = 4;
                break;
        }
        String value = message.getString("data");
        boolean isAlarm = false;
        String msg = "";
        String msgTitle = "";
        String deviceName = deviceService.getDeviceName(device);
        Update update = new Update();
        if (Const.INT_0.equals(cmdType)) {
            //心率
            update.set("lastHeart", value);
            //立即测量
            long time = DateUtil.strToTimestamp(message.getString("updateTime"), DateUtil.FORMAT_7) / 1000;
            HeartRateRecord record = heartRateRecordService.add(device.getId(), value);
            if (time == 0) {
                time = record.getTimestamp();
            }
            heartRateSettingService.getById(device.getId(), null);
            update.set("lastDataTime", time);
            heartRateSettingService.updateById(device.getId(), update);
            if (StringUtil.isNumeric(value) && (Integer.parseInt(value) > 100 || Integer.parseInt(value) < 60)) {
                isAlarm = true;
                msg = MessageUtils.getLocale(device.getLocale(), "API.pushTitle.health.HRAlarmContent", deviceName, value);
                msgTitle = MessageUtils.getLocale(device.getLocale(), "API.pushTitle.health.HRAlarmTitle");
            }
        } else if (Const.INT_1.equals(cmdType)) {
            //血压
            if (message.containsKey("historyData")) {
                //历史记录
                JSONArray historyData = message.getJSONArray("historyData");
                for (int i = 0; i < historyData.size(); i++) {
                    String[] valArr = historyData.getString(i).split("&");
                    if (valArr.length == 2) {
                        long time = DateUtil.strToTimestamp(valArr[0], DateUtil.FORMAT_7) / 1000;
                        BloodPressureRecord record = bloodPressureRecordService.get(device.getId(), valArr[1], time);
                        if (record == null) {
                            record = bloodPressureRecordService.add(device.getId(), valArr[1], time);
                        } else {
                            log.info("historyData is exists. imei={}, historyData={}", device.getId(), historyData.getString(i));
                        }
                    }
                }
            } else {
                //立即测量
                long time = DateUtil.strToTimestamp(message.getString("updateTime"), DateUtil.FORMAT_7) / 1000;
                BloodPressureRecord record = bloodPressureRecordService.add(device.getId(), value, time);
                if (time == 0) {
                    time = record.getDateTime();
                }
                bloodPressureSettingService.getById(device.getId(), null);
                update.set("currentValue", value);
                update.set("currentValueTime", time);
                bloodPressureSettingService.updateById(device.getId(), update);
                if (StringUtil.isNotBlank(value)) {
                    String[] tempArr = value.split("/");
                    if (tempArr.length >= 2) {
                        if ((StringUtil.isNumeric(tempArr[0]) && (Integer.parseInt(tempArr[0]) > 139 || Integer.parseInt(tempArr[0]) < 90))
                                || (StringUtil.isNumeric(tempArr[1]) && (Integer.parseInt(tempArr[1]) > 89 || Integer.parseInt(tempArr[1]) < 60))) {
                            isAlarm = true;
//                            msg = String.format("【%s】的设备血压测量异常，测量数值为%smmHg(结果仅供参考)", deviceName, value);
//                            msgTitle = "血压异常告警";
                            msg = MessageUtils.getLocale(device.getLocale(), "API.pushTitle.health.BPAlarmContent", deviceName, value);
                            msgTitle = MessageUtils.getLocale(device.getLocale(), "API.pushTitle.health.BPAlarmTitle");
                        } else if (tempArr.length >= 3 && StringUtil.isNumeric(tempArr[2])
                                && (Integer.parseInt(tempArr[2]) > 100 || Integer.parseInt(tempArr[2]) < 60)) {
                            isAlarm = true;
//                            msg = String.format("【%s】的设备脉搏测量异常，测量数值为%s次/分(结果仅供参考)", deviceName, value);
//                            msgTitle = "脉搏异常告警";
                            msg = MessageUtils.getLocale(device.getLocale(), "API.pushTitle.health.PulseAlarmContent", deviceName, value);
                            msgTitle = MessageUtils.getLocale(device.getLocale(), "API.pushTitle.health.PulseAlarmTitle");
                        }
                    }
                }
            }
        } else if (cmdType == 2) {
            //血氧
            update.set("currentValue", value);
            BloodOxygenRecord record = bloodOxygenRecordService.add(device.getId(), value);
            bloodOxygenSettingService.getById(device.getId(), null);
            update.set("currentValueTime", record.getDateTime());
            bloodOxygenSettingService.updateById(device.getId(), update);
            if (StringUtil.isNumeric(value) && (Integer.parseInt(value) > 100 || Integer.parseInt(value) < 70)) {
                isAlarm = true;
//                msg = String.format("【%s】的设备血氧测量异常，测量数值为%s%s(结果仅供参考)", deviceName, value, "%SpO2");
//                msgTitle = "血氧异常告警";
                msg = MessageUtils.getLocale(device.getLocale(), "API.pushTitle.health.BOAlarmContent", deviceName, value);
                msgTitle = MessageUtils.getLocale(device.getLocale(), "API.pushTitle.health.BOAlarmTitle");
            }
        } else if (cmdType == 3) {
            //体温
            update.set("lastTemperature", value);
            AnimalHeatRecord record = animalHeatRecordService.add(device.getId(), value);
            animalHeatSettingService.getById(device.getId(), null);
            update.set("lastDataTime", record.getTimestamp());
            animalHeatSettingService.updateById(device.getId(), update);
            if (StringUtil.isNumeric(value) && (Double.parseDouble(value) > Const.ANIMAL_HEAT_MAX
                    || Double.parseDouble(value) < Const.ANIMAL_HEAT_MIN)) {
                isAlarm = true;
//                msg = String.format("【%s】的设备体温测量异常，测量数值为%s℃(结果仅供参考)", deviceName, value);
//                msgTitle = "体温异常告警";
                msg = MessageUtils.getLocale(device.getLocale(), "API.pushTitle.health.AHAlarmContent", deviceName, value);
                msgTitle = MessageUtils.getLocale(device.getLocale(), "API.pushTitle.health.AHAlarmTitle");
            }
        } else {
            log.error("imei:{} 上传的指令cmdType不正确", device.getId());
            return;
        }
        if (isAlarm) {
            DeviceMessage deviceMessage = new DeviceMessage();
            deviceMessage.setImei(device.getId());
            deviceMessage.setName(deviceName);
            deviceMessage.setType("healthDataAlarm");
            deviceMessage.setValue(value);
            deviceMessage.setMsgTitle(msg);
            deviceMessage.setMsgContent(msg);
            deviceMessage.setTime(DateUtil.nowTimestampToDouble());
            deviceMessageService.save(deviceMessage);
        }
        if (CollectionUtil.isNotEmpty(device.getUserMap())) {
            for (String openid : device.getUserMap().keySet()) {
                if (isAlarm) {
                    //发送设置推送
                    JSONObject pushData = new JSONObject();
                    JSONObject messageJson = new JSONObject();
                    messageJson.put("imei", device.getId());
                    messageJson.put("type", "healthDataAlarm");
                    messageJson.put("name", deviceName);
                    messageJson.put("cmdType", cmdType);
                    messageJson.put("msgTitle", msgTitle);
                    messageJson.put("msgContent", msg);
                    messageJson.put("timestamp", System.currentTimeMillis() / 1000);
                    pushData.put("type", ConstKafka.ACCOUNT_MESSAGE);
                    pushData.put("message", messageJson);
                    kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_3, openid, pushData);
                } else {
                    String tempStr = String.format(CacheEnum.UpHeartRate.getKey(), device.getId(), openid);
                    if (cmdType == 3) {
                        tempStr = String.format(CacheEnum.UpBodyTemperature.getKey(), device.getId(), openid);
                    }
                    String cachOpenid = RedisAccountClusterService.getJedisCluster().get(tempStr);
                    if (openid.equals(cachOpenid)) {
                        JSONObject pushData = new JSONObject();
                        JSONObject messageJson = new JSONObject();
                        messageJson.put("imei", device.getId());
                        messageJson.put("type", message.getString("type"));
//                        messageJson.put("type", "healthUpdate");
                        messageJson.put("cmdType", cmdType);
                        messageJson.put("timestamp", System.currentTimeMillis() / 1000);
                        pushData.put("type", ConstKafka.ACCOUNT_MESSAGE);
                        pushData.put("message", messageJson);
                        kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_3, openid, pushData);
                    }
                }
            }
        }
    }

    /**
     * @param device
     * @param message
     * @time: 2021/1/28 17:49
     * @author: Rock
     * @version 1.0
     */
    private void type_107(Device device, JSONObject message) {
        // 可能是物联网卡 / 普通卡 (iccid)
        String cardId = message.getString("cardNumber");
        if (StringUtil.isBlank(cardId)) {
            return;
        }
        // 设备的 旧的物联网卡状态
        Integer oldIotStatus = device.getIsIOTCard();
        String oldCardId = null;
        if (oldIotStatus == 1) {
            //记录旧物联网卡的 cardId
            oldCardId = device.getIccid();
        }
        Update updateDevice = new Update();
        updateDevice.set("iccid", cardId);

        if (device.getWatchCustomFunction() != null && device.getWatchCustomFunction().contains("IOTCard:RJ")) {
            //判断是否是瑞集的物联网卡
            Object res = RuiJiIOTUtil.queryRJCardWhiteList(cardId);
            boolean isNewIotCard = false;
            IotCardInfo iotCardInfo = null;
            if (res instanceof JSONArray) {
                //是  瑞集的物联网卡
                //查询是否有物联网卡记录
                iotCardInfo = iotCardInfoService.findById(cardId);
                if (iotCardInfo == null) {
                    iotCardInfo = new IotCardInfo();
                    iotCardInfo.setId(cardId);
                    iotCardInfo.setCardNum(cardId);
                    iotCardInfo.setVendor("瑞集物联网卡");
                }

                JSONArray moblies = (JSONArray) res;
                if (moblies.size() == 0) {
                    //白名单为0的则为新卡
                    isNewIotCard = true;
                    //因为下面为0时不会保存 新对象
                    iotCardInfoService.save(iotCardInfo);
                }
                iotCardInfoService.updareContacts(iotCardInfo, moblies);
                updateDevice.set("isIOTCard", 1);

            } else if (res instanceof String) {
                //错误, 认为不是
                //非物联网卡
                updateDevice.set("isIOTCard", 0);
                deviceService.updateById(device.getId(), updateDevice);
                //如果是  物联网卡 ===> 普通卡 也需要同步一下通讯录
                if (oldIotStatus == 1) {
                    //同步一下通讯录
                    JSONObject kafkaJson = new JSONObject();
                    kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_Contact, kafkaJson, null);
                }
                return;
            }
            deviceService.updateById(device.getId(), updateDevice);
            Map<String, Map<String, Object>> userMap = device.getUserMap();
            if (userMap == null || userMap.isEmpty()) {
                //无 绑定用户 不进行这种 同步号码 操作
                log.info("imei:{} 无 绑定用户 不进行这种 同步号码 操作 新卡(cardId:{}), 旧卡(oldCardId:{})", device.getId(), cardId, oldCardId);
                return;
            }
            if (isNewIotCard && !cardId.equals(oldCardId)) {
                //如果是新卡，且新旧 不一样 需要进行号码同步，如果号码数量大于5个，则同步前5个号码
                log.info("imei:{} 如果是新卡(cardId:{}), 旧卡(oldCardId:{})，且新旧 不一样 需要进行号码同步", device.getId(), cardId, oldCardId);
                int limitNum = 5;

                //需要添加的名单
                List<String> tempPhoneList = new ArrayList<>(); // 去重用
                List<Map<String, Object>> endAPPUserList = new ArrayList<>();
                List<Map<String, Object>> endOldContactList = new ArrayList<>();
                List<Map<String, Object>> endContactList = new ArrayList<>();
                List<Map<String, Object>> endAllList = new ArrayList<>();
                //优先 管理员 / 关注者
                /*
                "u" : {
                        "2c9d73b2898bb77301898bb773510000" : {
                                "image" : 1,
                                "c" : "2c9d73b2898bb77301898bb773510000",
                                "t" : NumberLong(1709084549),
                                "phone" : "13418100840",
                                "identity" : "admin",
                                "openid" : "2c9d73b2898bb77301898bb773510000",
                                "g" : "65a77ddfd82f6475c712686b",
                                "name" : "爸爸",
                                "canVideo" : 1,
                                "relationship" : "爸爸",
                                "relationship_image_id" : 1
                        },
                        "b67a574c263806bb491bd112" : {
                                "image" : 2,
                                "c" : "b67a574c263806bb491bd112",
                                "t" : 1710814712.461,
                                "phone" : "13418100841",
                                "identity" : "member",
                                "openid" : "b67a574c263806bb491bd112",
                                "g" : "65a77ddfd82f6475c712686b",
                                "name" : "妈妈",
                                "canVideo" : 1,
                                "relationship" : "妈妈",
                                "relationship_image_id" : 2
                        }
                }
                */
                for (Map<String, Object> item : userMap.values()) {
                    JSONObject tempItem = new JSONObject(item);
                    tempPhoneList.add(tempItem.getString("phone"));
                    endAPPUserList.add(tempItem);
                }
                //排序 2:降序
                CollectionUtil.sortDouble(endAPPUserList, "t", 2);

                if (oldCardId != null) {
                    //物联网SIM卡 转 物联网SIM卡
                    //原有的是物联网卡
                    IotCardInfo oldIotCardInfo = iotCardInfoService.findById(oldCardId);
                    //旧卡原有的手机号
                    Map<String, Map<String, Object>> oldContactMap = oldIotCardInfo.getContactMap();
                    if (oldContactMap != null && !oldContactMap.isEmpty()) {
                        //需要添加
                        for (Map<String, Object> item : oldContactMap.values()) {
                            JSONObject tempItem = new JSONObject(item);
                            String phone = tempItem.getString("phone");
                            //得去重, 防止和 endAPPUserList 重复
                            if (tempPhoneList.contains(phone)) {
                                //重复就跳过
                                continue;
                            }
                            tempPhoneList.add(phone);
                            endOldContactList.add(tempItem);
                        }
                        //排序 2:降序
                        CollectionUtil.sortDouble(endOldContactList, "t", 2);
                    }
                } else {
                    //非物联网SIM卡 转 物联网SIM卡
                    //取原有的 通讯录名单
                    Map<String, Map<String, Object>> contactMap = device.getContactMap();
                    for (Map<String, Object> item : contactMap.values()) {
                        JSONObject tempItem = new JSONObject(item);
                        String phone = tempItem.getString("phone");
                        //得去重, 防止和 endAPPUserList 重复
                        if (tempPhoneList.contains(phone)) {
                            //重复就跳过
                            continue;
                        }
                        tempPhoneList.add(phone);
                        endContactList.add(tempItem);
                    }
                    //排序 2:降序
                    CollectionUtil.sortDouble(endContactList, "t", 2);
                }
                //加一起
                endAllList.addAll(endAPPUserList);
                endAllList.addAll(endOldContactList);
                endAllList.addAll(endContactList);
                if (limitNum > endAllList.size()) {
                    limitNum = endAllList.size();
                }
                StringBuilder endAllPhone = new StringBuilder();

                Map<String, Map<String, Object>> tempSucMap = new HashMap<>();
                for (int i = 0; i < limitNum; i++) {

                    JSONObject addItem = new JSONObject(endAllList.get(i));
                    Map<String, Object> item = new HashMap<>();
                    String phone = addItem.getString("phone");
                    item.put("phone", phone);
                    item.put("name", addItem.getString("name"));
                    item.put("t", DateUtil.nowTimestampToLong());
                    //成功, 昵称 +手机号存储起来
                    tempSucMap.put(phone, item);

                    if (i != (limitNum - 1)) {
                        endAllPhone.append(phone + "_");
                    } else {
                        endAllPhone.append(phone);
                    }
                }
                //去物联网卡运营商 添加
                Object addRes = RuiJiIOTUtil.doRJCardWhiteList(cardId, endAllPhone.toString(), true);
                if (addRes instanceof Boolean) {
                    iotCardInfo.getContactMap().putAll(tempSucMap);
                } else {
                    //失败,
                    log.error("瑞集物联卡 换卡 同步通讯录时 失败: {} 不进行后续同步 break", addRes);
                }
                iotCardInfoService.save(iotCardInfo);
            }

            JSONObject kafkaJson = new JSONObject();
            kafkaJson.put("handleFlag", 1);
            kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_Contact, kafkaJson, null);
        }

//        else if (cardId.length() >= 4 && "8986".equals(cardId.substring(0, 4))) {
//            JSONObject resData = CXUtil.getCXCardInfo(cardId);
//            if (resData != null && resData.size() > 0) {
//                if (Const.INT_1.equals(resData.getInteger("code"))) {
//                    IotCardInfo cardInfo = new IotCardInfo();
//                    //查询一下最新记录的白名单
//                    JSONObject body = CXUtil.queryCXCardWhiteList(cardId);
//                    if (body != null && !body.isEmpty()) {
//                        //更新在库中记录的白名单
//                        iotCardInfoService.addContacts(iotCardInfo, body.getJSONArray("mobile"), body.getJSONArray("nick"));
//                    } else {
//                        log.error("CXUtil.queryCXCardWhiteList is fail. message={}", message);
//                    }
//                    cardInfo.setId(cardId);
//                    cardInfo.setVendor("畅行");
//                    cardInfo.setOperator(resData.getString("carrierOperatorName"));
//                    cardInfo.setCardNum(resData.getString("cardNumber"));
//                    Map<String, Object> tempMap = new HashMap<>();
//                    Iterator it = resData.entrySet().iterator();
//                    while (it.hasNext()) {
//                        String key = (String) it.next();
//                        tempMap.put(key, resData.get(key));
//                    }
//                    cardInfo.setVendorCardInfo(tempMap);
//                    iotCardInfoService.save(cardInfo);
//                    updateDevice.set("isIOTCard", 1);
//                } else if (new Integer(-3).equals(resData.getInteger("code"))) {
//                    log.info("imei:{} 查询成功  但不是CX他们公司的卡", device.getId());
//                    updateDevice.set("isIOTCard", 0);
//                } else if (new Integer(-200).equals(resData.getInteger("code"))) {
//                    log.error("imei:{} 查询失败 CX 他们的接口没回应", device.getId());
//                } else {
//                    log.error("imei:{} 查询失败 resData: {}", device.getId(), resData);
//                    updateDevice.set("isIOTCard", 0);
//                }
//            } else {
//                updateDevice.set("isIOTCard", 0);
//            }
//        } else {
//            updateDevice.set("isIOTCard", 0);
//        }

    }

    /**
     * 代收短信信息
     *
     * @param device
     * @param message
     * @time: 12/14/21 4:58 PM
     * @author: Weichao
     * @version 1.0
     */
    private void type_upSMS(Device device, JSONObject message) {
        //更新设备的代收短信的状态
        deviceService.updateCmsgStatusById(device.getId());
        String fromPhone = message.getString("fromPhone");
        String key = device.getId() + "-" + fromPhone + Constant.INQUIRE_PHONE_CHARGE_CACHE_POSTFIX;
        String cacheData = redisFilterService.get(key);
        if (StringUtils.isEmpty(cacheData)) {
            //添加短信记录
            deviceCmsgService.add(device.getId(), message, 0);
            //给手机推送消息
            pushAccountMessage(device, "sms", "API.pushTitle.sms", null, null, true);
        } else {
            //添加短信记录
            deviceCmsgService.add(device.getId(), message, 1);
            //给手机推送消息
            pushAccountMessage(device, "inquireChargeSms", "API.pushTitle.inquireChargeSms", null, null, true);
            //删除缓存
            redisFilterService.del(key);
        }

    }

    /**
     * @param [device, message]
     * @return void
     * @description WIFI搜索结果上报
     * @author cwj
     * @date @time 2024/12/19 9:23
     **/
    private void type_wifiSearch(Device device, JSONObject message) {
        //缓存上报结果
        String content = message.getString("content");
        String key = Constant.WIFI_SEARCH_PREFIX + device.getId();
        //此纪录保存60s
        redisFilterService.setex(key, Constant.WIFI_SEARCH_DATA_CACHE_TIME, content);
        String openIdKey = Constant.WIFI_SEARCH_OPENID_PREFIX + device.getId();
        //获取指令下发时缓存的openid
        String openId = getCacheOpenId(openIdKey);
        //推送消息
        pushAccountMessage(device, "wifiSearch", "API.pushTitle.wifiSearch", openId, null, false);
    }

    /**
     * @param [key]
     * @return java.lang.String
     * @description 获取缓存的openId
     * @author cwj
     * @date @time 2024/12/24 9:42
     **/
    private String getCacheOpenId(String key) {
        String id = "";
        String idStr = redisFilterService.get(key);
        if (!StringUtils.isEmpty(idStr)) {
            String[] ids = idStr.split(",");
            id = ids[0];
            redisFilterService.del(key);
            StringBuilder sb = new StringBuilder();
            //去掉第一个openId或id
            for (int i = 1; i < ids.length; i++) {
                sb.append(ids[i]);
                if (i < ids.length - 1) {
                    sb.append(",");
                }
            }
            if (ObjectUtils.isNotEmpty(sb)) {
                redisFilterService.set(key, sb.toString());
            }
        }
        return id;
    }


    /**
     * @param [device, message]
     * @return void
     * @description wifi设置应答
     * @author cwj
     * @date @time 2024/12/20 14:07
     **/
    private void type_wifiSet(Device device, JSONObject message) {
        String idKey = Constant.WIFI_SET_ID_PREFIX + device.getId();
        //id只会缓存60s，超过60s没回应答，则认为指令设置失败
        String id = redisFilterService.get(idKey);
        SetWifiInfo setWifiInfo = setWifiService.queryRecentlySetWifiInfo(device.getId(), id);
        if (ObjectUtils.isNotEmpty(setWifiInfo)) {
            //更新设置的状态
            setWifiService.updateSetWifiInfoIsSuccess(setWifiInfo.getId(), message.getInteger("isSuccess"));
            String openIdKey = Constant.WIFI_SET_OPENID_PREFIX + device.getId();
            //获取指令下发时缓存的openid
            String openId = getCacheOpenId(openIdKey);
            pushAccountMessage(device, "wifiSet", "API.pushTitle.wifiSet", openId, null, false);
        }
    }

    /**
     * @param [device, message]
     * @return void
     * @description 查询设备当前连接的wifi信息
     * @author cwj
     * @date @time 2024/12/20 11:48
     **/
    private void type_wifiCur(Device device, JSONObject message) {
        //缓存上报结果
        String content = message.getString("content");
        String key = Constant.WIFI_CURRENT_CONNECT_PREFIX + device.getId();
        //此纪录保存30s
        redisFilterService.setex(key, Constant.WIFI_CURRENT_CONNECT_DATA_CACHE_TIME, content);
        String openIdKey = Constant.WIFI_CUR_OPENID_PREFIX + device.getId();
        //获取指令下发时缓存的openid
        String openId = getCacheOpenId(openIdKey);
        pushAccountMessage(device, "wifiCur", "API.pushTitle.wifiCur", openId, content, false);
    }

    /**
     * @param [device]
     * @return void
     * @description 推送AccountMessage
     * @author cwj
     * @date @time 2024/12/18 10:03
     **/
    private void pushAccountMessage(Device device, String messageType, String title, String openId, String content, boolean isSaveMessage) {
        String deviceName = deviceService.getDeviceName(device);
        String msg = MessageUtils.getLocale(device.getLocale(), title, deviceName);
        if (isSaveMessage) {
            DeviceMessage deviceMessage = new DeviceMessage();
            deviceMessage.setImei(device.getId());
            deviceMessage.setType(messageType);
            deviceMessage.setName(deviceName);
            deviceMessage.setMsgTitle(msg);
            deviceMessage.setMsgContent(StringUtils.isEmpty(content) ? msg : content);
            deviceMessageService.save(deviceMessage);
        }
        if (CollectionUtil.isNotEmpty(device.getUserMap())) {
            //发送设置推送
            JSONObject pushData = new JSONObject();
            JSONObject messageJson = new JSONObject();
            messageJson.put("imei", device.getId());
            messageJson.put("type", messageType);
            messageJson.put("name", deviceName);
            messageJson.put("msgTitle", msg);
            messageJson.put("msgContent", StringUtils.isEmpty(content) ? msg : content);
            messageJson.put("timestamp", DateUtil.nowTimestampToDouble());
            pushData.put("type", ConstKafka.ACCOUNT_MESSAGE);
            pushData.put("message", messageJson);
            if (StringUtils.isEmpty(openId)) {
                for (String openid : device.getUserMap().keySet()) {
                    kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_3, openid, pushData);
                }
            } else {
                kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_3, openId, pushData);
            }
        }
    }


    /**
     * @param [device, messageType, title, openId, content, isSaveMessage, isPushOnlyVip]
     * @return void
     * @description 推送AccountMessage
     * @author cwj
     * @date @time 2025/3/23 9:27
     **/
    private void pushAccountMessage2(Device device, String messageType, String title, String openId, String content, boolean isSaveMessage, boolean isPushOnlyVip) {
        String deviceName = deviceService.getDeviceName(device);
        String msg = MessageUtils.getLocale(device.getLocale(), title, deviceName);
        if (isSaveMessage) {
            DeviceMessage deviceMessage = new DeviceMessage();
            deviceMessage.setImei(device.getId());
            deviceMessage.setType(messageType);
            deviceMessage.setName(deviceName);
            deviceMessage.setMsgTitle(msg);
            deviceMessage.setMsgContent(StringUtils.isEmpty(content) ? msg : content);
            deviceMessageService.save(deviceMessage);
        }
        if (CollectionUtil.isNotEmpty(device.getUserMap())) {
            //发送设置推送
            JSONObject pushData = new JSONObject();
            JSONObject messageJson = new JSONObject();
            messageJson.put("imei", device.getId());
            messageJson.put("type", messageType);
            messageJson.put("name", deviceName);
            messageJson.put("msgTitle", msg);
            messageJson.put("msgContent", StringUtils.isEmpty(content) ? msg : content);
            messageJson.put("timestamp", DateUtil.nowTimestampToDouble());
            pushData.put("type", ConstKafka.ACCOUNT_MESSAGE);
            pushData.put("message", messageJson);
            if (isPushOnlyVip) {
                if (StringUtils.isEmpty(openId)) {
                    if (!CollectionUtils.isEmpty(device.getUserMap().keySet())) {
                        List<VipMember> vipMembers = vipMemberService.queryVipMemberByOpenids(device.getUserMap().keySet());
                        Map<String, VipMember> vipMemberMap = new ConcurrentHashMap<>();
                        if (!CollectionUtils.isEmpty(vipMembers)) {
                            vipMemberMap = vipMembers.stream().collect(Collectors.toMap(VipMember::getOpenid, Function.identity()));
                        }
                        for (String openid : device.getUserMap().keySet()) {
                            if (!CollectionUtils.isEmpty(vipMemberMap) && ObjectUtils.isNotEmpty(vipMemberMap.get(openid))) {
                                kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_3, openid, pushData);
                            }
                        }
                    }
                } else {
                    VipMember vipMember = vipMemberService.queryVipMemberByOpenid(openId);
                    if (ObjectUtils.isNotEmpty(vipMember)) {
                        kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_3, openId, pushData);
                    }
                }
            } else {
                if (StringUtils.isEmpty(openId)) {
                    for (String openid : device.getUserMap().keySet()) {
                        kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_3, openid, pushData);
                    }
                } else {
                    kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_3, openId, pushData);
                }
            }

        }
    }


    /**
     * 设备远程拍照上传
     *
     * @param device
     * @param message
     * @time: 2021/1/22 18:50
     * @author: Rock
     * @version 1.0
     */
    private void type_upRemoteImage(Device device, JSONObject message) {
        DevicePhotograph devicePhotograph = new DevicePhotograph();
        devicePhotograph.setImei(device.getId());
        devicePhotograph.setImage(message.getString("remoteImagePath"));
        devicePhotograph.setThumb(message.getString("remoteImagePath"));
        devicePhotographService.save(devicePhotograph);
        String deviceName = deviceService.getDeviceName(device);
        String msg = MessageUtils.getLocale(device.getLocale(), "API.pushTitle.remoteImage", deviceName);
        String msgContent = msg;
        DeviceMessage deviceMessage = new DeviceMessage();
        deviceMessage.setImei(device.getId());
        deviceMessage.setName(deviceName);
        deviceMessage.setType("photograph");
        deviceMessage.setPhotoID(new ObjectId(devicePhotograph.getId()));
        deviceMessage.setImage(devicePhotograph.getImage());
        deviceMessage.setThumb(devicePhotograph.getThumb());
        deviceMessage.setMsgTitle(msg);
        deviceMessage.setMsgContent(msgContent);
        deviceMessageService.save(deviceMessage);
        if (CollectionUtil.isNotEmpty(device.getUserMap())) {
            JSONObject data = new JSONObject();
            JSONObject subJson = new JSONObject();
            data.put("type", ConstKafka.ACCOUNT_MESSAGE);
            subJson.put("id", deviceMessage.getId());
            subJson.put("imei", device.getId());
            subJson.put("type", "photograph");
            subJson.put("name", deviceName);
            subJson.put("photographID", devicePhotograph.getId());
            subJson.put("url", baseBucketTool.getDOMAIN() + BaseBucketTool.PHOTOGRAPH_COMMON_PATH + devicePhotograph.getImage());
            subJson.put("thumbURL", baseBucketTool.getDOMAIN() + BaseBucketTool.PHOTOGRAPH_COMMON_PATH + devicePhotograph.getThumb());
            subJson.put("msgTitle", msg);
            subJson.put("msgContent", msgContent);
            subJson.put("timestamp", System.currentTimeMillis() / 1000);
            data.put("message", subJson);
            for (String openid : device.getUserMap().keySet()) {
                kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_3, openid, data);
            }
        }
    }

    /**
     * new 整合版 设备上传 和APP的聊天消息
     *
     * @param device
     * @param message
     * @time: 2023/12/08 14:50
     * @author: Weichao
     * @version 1.0
     */
    private void type_newGroupmsg(Device device, JSONObject message) {

        /**
         *          message
         * imei   			: <string> 	//设备imei参数
         *
         * groupId			: <string> 	//发送的群组ID (当groupType==2时, 为对端好友的IMEI号)
         * groupType		: <int>  		//模式 0:群聊 1:单聊 2:碰碰交友聊天
         * content*		: <file> 		//数据
         * contentType : <int>			//  1:文字 2: 图片 3:语音 4:表情
         * bqId*				: <string>	//表情ID (contentType:2 时才会有)
         * size*				: <int>  		//数据大小
         * audioDuration*: <int>  	//语音时长 (contentType:0 时才会有)(为了和APP上的语音时长统一)
         * ident				: <int>  		//6位随机数正整数
         */

        Integer groupType = message.getInteger("groupType");
        // 0:语音 1:图片 2:表情  (手表没法发文字)
        Integer contentType = message.getInteger("contentType");

        String groupId = message.getString("groupId");
        //要推送的内容
        JSONObject subPushJson = new JSONObject();
        subPushJson.put("groupType", groupType);
        subPushJson.put("groupId", groupId);
        if (groupType == 0) {
            ChatGroup chatGroup = chatGroupService.getChatGroupByDevice(device);
            if (chatGroup == null) {
                log.warn("imei:{} 的找不到对应群组, return", device.getId());
                return;
            }
            //群聊
            ObjectId gid = new ObjectId(groupId);
            //表拥有的群组 都插入一条聊天消息
            ChatGroupMessage chatGroupMessage = new ChatGroupMessage();
            chatGroupMessage.setGroupid(gid);
            //1:用户 2:设备
            chatGroupMessage.setSendType(2);
            chatGroupMessage.setAvator(baseBucketTool.getDOMAIN() + BaseBucketTool.DEVICE_COMMON_PATH
                    + (StringUtil.isBlank(device.getImage()) ? Const.DEFAULT_JPG : device.getImage()));
            chatGroupMessage.setSender(device.getId());
            chatGroupMessage.setMessageType(contentType);
            chatGroupMessage.setContent(message.getString("content"));
            subPushJson.put("content", message.get("content"));

            switch (contentType) {
//                case 1:
//                    break;
                case 2:
                case 3:
                    subPushJson.put("content", baseBucketTool.getDOMAIN() + BaseBucketTool.MESSAGE_COMMON_PATH + message.get("content"));
                    chatGroupMessage.setSize(message.getLong("size"));
                    if (message.containsKey("duration")) {
                        chatGroupMessage.setDuration(message.getDouble("duration"));
                        subPushJson.put("duration", message.get("duration"));
                    }
                    break;
                case 4:
                    chatGroupMessage.setBqId(message.getString("bqId"));
                    subPushJson.put("bqId", message.getString("bqId"));
                    break;
            }
            if (contentType == 3) {
                //检测敏感词-语音====
                if (!contentSecurityUtil.chatAudioAudit(subPushJson.getString("content"), device.getId(), groupId, chatGroupMessage.getId())) {
                    //保存群聊敏感语音信息记录
                    sensitivityFileGroupMessageService.saveData(groupId, chatGroupMessage);
                    //检测敏感词-语音====
                    log.error("上报信息中包含敏感信息，不能推送此消息给APP用户，含有敏感信息的文件链接为：{}",
                            baseBucketTool.getDOMAIN() + BaseBucketTool.MESSAGE_COMMON_PATH + message.get("content"));
                    return;
                }
            }
            chatGroupMessageService.add(chatGroupMessage);

            if (CollectionUtil.isNotEmpty(chatGroup.getMemberMap())) {
                //给所有的绑定这个群组的用户 加未读消息 +1
                FindIterable<Document> accountDocs = accountService.listDocByIds(chatGroup.getMemberMap().keySet());
                for (Document doc : accountDocs) {
                    Update updateAccount = new Update();
                    updateAccount.set(groupId, doc.getInteger(groupId) == null ? 1 : doc.getInteger(groupId) + 1);

                    updateAccount.set("badge", doc.getInteger("badge") == null ? 1 : doc.getInteger("badge") + 1);
                    accountService.updateById(doc.getString("_id"), updateAccount);
//                    //表消息推送到微信公众号，用于微信公众号聊天
//                    if (StringUtil.isNotBlank(doc.get("wxgz_ident")) && !"0".equals(doc.get("wxgz_ident").toString())) {
//                        weixinService.replyWxMsg(chatGroupMessage.getMessageType(), chatGroupMessage.getContent(),
//                                accountService.findById(doc.getString("_id")));
//                    }
                }
            }
            subPushJson.put("id", chatGroupMessage.getId());
            subPushJson.put("senderID", device.getId());
            subPushJson.put("senderType", 2);
            subPushJson.put("type", chatGroupMessage.getMessageType());
            subPushJson.put("timestamp", DateUtil.nowTimestampToLong());
        } else if (groupType == 1) {
            //单聊
            SingleChatGroupMessage chatGroupMessage = new SingleChatGroupMessage();
            chatGroupMessage.setGroupid(groupId);
            //1:用户 2:设备
            chatGroupMessage.setSendType(2);
            chatGroupMessage.setSender(device.getId());
            chatGroupMessage.setMessageType(contentType);
            chatGroupMessage.setContent(message.getString("content"));
            subPushJson.put("content", message.get("content"));
            switch (contentType) {
//                case 1:
//                    break;
                case 2:
                case 3:
                    subPushJson.put("content", baseBucketTool.getDOMAIN() + BaseBucketTool.MESSAGE_COMMON_PATH + message.get("content"));
                    chatGroupMessage.setSize(message.getLong("size"));
                    if (message.containsKey("duration")) {
                        chatGroupMessage.setDuration(message.getDouble("duration"));
                        subPushJson.put("duration", message.get("duration"));
                    }
                    break;
                case 4:
                    chatGroupMessage.setBqId(message.getString("bqId"));
                    subPushJson.put("bqId", message.getString("bqId"));
                    break;
            }
            subPushJson.put("type", chatGroupMessage.getMessageType());
            subPushJson.put("senderID", device.getId());
            subPushJson.put("senderType", 2);
            if (contentType == 3) {
                //检测敏感词-语音====
                if (!contentSecurityUtil.chatAudioAudit(subPushJson.getString("content"), device.getId(), groupId, chatGroupMessage.getId())) {
                    //保存群聊敏感语音信息记录
                    sensitivityFileSingleMessageService.saveData(groupId, chatGroupMessage);
                    //检测敏感词-语音====
                    log.error("上报信息中包含敏感信息，不能推送此消息给APP用户，含有敏感信息的文件链接为：{}",
                            baseBucketTool.getDOMAIN() + BaseBucketTool.MESSAGE_COMMON_PATH + message.get("content"));
                    return;
                }
            }
            SingleChatGroup s_group = singleChatGroupService.findById(groupId);
            //更新下未读消息 因为单聊的只有一个,所以APP的未读消息记录在这个 singleChatGroup 上
            Integer badge = s_group.getBage();
            s_group.setBage(++badge);
            singleChatGroupService.save(s_group);
            singleChatGroupMessageService.add(chatGroupMessage);
            subPushJson.put("id", chatGroupMessage.getId());
        } else if (groupType == 2) {
            //碰碰交友
            message.put("pp_imei", message.getString("groupId"));
            type_upPPMsg(device, message);
            return;
        }
        //发送kafka
        subPushJson.put("msgTitle", MessageUtils.getLocale(device.getLocale(), "API.pushTitle.newChatMsg"));
        subPushJson.put("timestamp", DateUtil.nowTimestampToDouble());
        JSONObject pushData = new JSONObject();
        pushData.put("type", ConstKafka.CHAT_GROUP_MESSAGE);
        pushData.put("groupid", groupId);
        pushData.put("message", subPushJson);
        kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_1, groupId, pushData);
    }

    /**
     * 设备上传 和APP的聊天消息
     *
     * @param device
     * @param message
     * @time: 2021/1/23 14:50
     * @author: Rock
     * @version 1.0
     */
    private void type_groupmsg(Device device, JSONObject message) {
        //要推送的内容
        JSONObject subPushJson = new JSONObject();
        String updateType = message.getString("type");
        ChatGroup chatGroup = chatGroupService.getChatGroupByDevice(device);
        if (chatGroup == null) {
            log.warn("imei:{} 的找不到对应群组, return", device.getId());
            return;
        }
        Set<String> openids = chatGroup.getMemberMap().keySet();
        //检查成员中有没有会员
        List<VipMember> vipMembers = vipMemberService.queryVipMemberByOpenids(openids);
        int todayVoiceTimes = 0;
        Calendar calendar = Calendar.getInstance();
        String todayKey = device.getId() + DateUtil.dateToStr(calendar.getTime(), DateUtil.FORMAT_5);
        JSONObject jsonData = checkVoiceTimeUpLimit(calendar, device.getId(), todayKey, todayVoiceTimes);

        String groupId = chatGroup.getId();
        if ("upChatVoice".equals(updateType) || "chatImage".equals(updateType)) {
            //表拥有的群组 都插入一条聊天消息
            ChatGroupMessage chatGroupMessage = new ChatGroupMessage();
            ObjectId gid = new ObjectId(groupId);
            chatGroupMessage.setGroupid(gid);
            //1:用户 2:设备
            chatGroupMessage.setSendType(2);
            chatGroupMessage.setAvator(baseBucketTool.getDOMAIN() + BaseBucketTool.DEVICE_COMMON_PATH
                    + (StringUtil.isBlank(device.getImage()) ? Const.DEFAULT_JPG : device.getImage()));
            chatGroupMessage.setSender(device.getId());
            chatGroupMessage.setChatReportTime(DateUtil.stampToDateStr(DateUtil.nowTimestampToLong(), DateUtil.FORMAT_1));
            //设置记录是否超过限制
            if (!CollectionUtils.isEmpty(vipMembers) || jsonData.getBooleanValue("isAllow")) {
                chatGroupMessage.setIsExceedLimitRecord(1);
            } else {
                chatGroupMessage.setIsExceedLimitRecord(0);
            }
            // 转换一下
            // 1:文字,2:图片,3:语音.4:表情包
            switch (updateType) {
                case "upChatVoice": {
                    //语音
                    chatGroupMessage.setMessageType(3);
                    chatGroupMessage.setContent(message.getString("voicePath"));
                    chatGroupMessage.setSize(message.getLong("size"));
                    chatGroupMessage.setDuration(message.getDouble("duration"));
                    chatGroupMessage.setDuration(message.getDouble("duration"));
                    subPushJson.put("content", baseBucketTool.getDOMAIN() + BaseBucketTool.MESSAGE_COMMON_PATH + message.get("voicePath"));
                    subPushJson.put("duration", message.get("duration"));
                }
                break;
                case "chatImage": {
                    //图片
                    chatGroupMessage.setMessageType(2);
                    chatGroupMessage.setContent(message.getString("imagePath"));
                    chatGroupMessage.setSize(message.getLong("size"));
                    chatGroupMessage.setDuration(message.getDouble("duration"));
                    chatGroupMessage.setDuration(0.0);
                    subPushJson.put("content", baseBucketTool.getDOMAIN() + BaseBucketTool.MESSAGE_COMMON_PATH + message.get("imagePath"));
                }
                break;
            }
            if ("upChatVoice".equalsIgnoreCase(updateType)) {
                //检测敏感词-语音====
                if (!contentSecurityUtil.chatAudioAudit(subPushJson.getString("content"), device.getId(), groupId, chatGroupMessage.getId())) {
                    //保存群聊敏感语音信息记录
                    sensitivityFileGroupMessageService.saveData(groupId, chatGroupMessage);
                    //检测敏感词-语音====
                    log.error("上报信息中包含敏感信息，不能推送此消息给APP用户，含有敏感信息的文件链接为：{}",
                            baseBucketTool.getDOMAIN() + BaseBucketTool.MESSAGE_COMMON_PATH + message.get("voicePath"));
                    return;
                }
            }
            //保存语音微聊信息
            chatGroupMessageService.add(chatGroupMessage);
            if (CollectionUtil.isNotEmpty(chatGroup.getMemberMap())) {
                //给所有的绑定这个群组的用户 加未读消息 +1
                FindIterable<Document> accountDocs = accountService.listDocByIds(chatGroup.getMemberMap().keySet());
                for (Document doc : accountDocs) {
                    Update updateAccount = new Update();
                    if (!CollectionUtils.isEmpty(vipMembers) || (CollectionUtils.isEmpty(vipMembers) && jsonData.getBooleanValue("isAllow"))) {
                        //非会员且每天未超过限制次数时的未读记录次数
                        String field = Constant.NO_VIP_UNREAD_TIME_PREFIX + groupId;
                        updateAccount.set(field, doc.getInteger(field) == null ? 1 : doc.getInteger(field) + 1);
                    }
                    updateAccount.set(groupId, doc.getInteger(groupId) == null ? 1 : doc.getInteger(groupId) + 1);
                    updateAccount.set("badge", doc.getInteger("badge") == null ? 1 : doc.getInteger("badge") + 1);
                    accountService.updateById(doc.getString("_id"), updateAccount);
//                    //表消息推送到微信公众号，用于微信公众号聊天
//                    if (StringUtil.isNotBlank(doc.get("wxgz_ident")) && !"0".equals(doc.get("wxgz_ident").toString())) {
//                        weixinService.replyWxMsg(chatGroupMessage.getMessageType(), chatGroupMessage.getContent(),
//                                accountService.findById(doc.getString("_id")));
//                    }
                }
            }
            subPushJson.put("id", chatGroupMessage.getId());
            subPushJson.put("senderID", device.getId());
            subPushJson.put("senderType", 2);
            subPushJson.put("type", chatGroupMessage.getMessageType());
            subPushJson.put("timestamp", DateUtil.nowTimestampToLong());
        } else if ("upPpVoice".equals(updateType) || "upPpImage".equals(updateType)) {
            // 碰碰交友
            type_upPPMsg(device, message);
            return;
        }
        //发送kafka
        subPushJson.put("msgTitle", MessageUtils.getLocale(device.getLocale(), "API.pushTitle.newChatMsg"));
        subPushJson.put("timestamp", DateUtil.nowTimestampToDouble());
        JSONObject pushData = new JSONObject();
        pushData.put("type", ConstKafka.CHAT_GROUP_MESSAGE);
        pushData.put("groupid", groupId);
        pushData.put("isNewWechatProtocol", device.getIsNewWechatProtocol());
        pushData.put("message", subPushJson);
        //控制消息推送，检查imei绑定的用户有没有会员
        if (CollectionUtil.isNotEmpty(chatGroup.getMemberMap())) {
            if (!CollectionUtils.isEmpty(vipMembers)) {
                kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_1, groupId, pushData);
            } else {
                //检查是否允许推送记录
                if (jsonData.getBooleanValue("isAllow")) {
                    todayVoiceTimes = jsonData.getIntValue("todayVoiceTimes");
                    log.info("今日已经下发记录次数:{}", todayVoiceTimes);
                    kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_1, groupId, pushData);
                    todayVoiceTimes++;
                    redisFilterService.setex(todayKey, 24 * 60 * 60, String.valueOf(todayVoiceTimes));
                } else {
                    //推送消息告诉用户，因为没有购买会员，今天已经到达推送消息的上限，设备消息无法再推送过来，提醒家长，群聊消息只需一个家长购买会员即可享受不受限制的设备微聊信息的推送
                    pushAccountMessage(device, "weChatMsg", "API.pushTitle.wechat.limit", null, "设备的微聊记录推送次数已达上限，建议您充值成为会员解锁推送限制", true);
                }
            }
        }
    }

    /**
     * @param [device, message]
     * @return void
     * @description 处理设备上报上来的碰碰好友消息
     * @author cwj
     * @date @time 2025/7/6 16:17
     **/
    private void type_ppSingleGroupMsg(Device device, JSONObject message) {
        //要推送的内容
        String updateType = message.getString("type");
        String imei2 = ObjectUtils.isNotEmpty(message.get("otherImei")) ? message.getString("otherImei") : null;
        String shortId2 = message.getString("id");
        if (StringUtils.isEmpty(imei2)) {
            imei2 = redisClusterService.get("SHORT:" + shortId2);
        }
        if (StringUtils.isEmpty(imei2)) {
            log.error("设备：{}发送失败，待接收消息的设备:{}未在系统中注册或已删除", device.getId(), shortId2);
            return;
        }
        Device device2 = deviceService.findById(imei2);
        if (ObjectUtils.isEmpty(device2)) {
            log.error("设备：{}发送失败，待接收消息的设备：{}未在系统中注册或已删除", device.getId(), imei2);
            //发送删除设备碰碰好友的指令
            delIme1PpFriend(device.getId(), imei2, device2.getPhone());
            return;
        }
        if (MapUtils.isEmpty(device.getPpFriends()) || MapUtils.isEmpty(device2.getPpFriends())) {
            log.error("设备消息发送失败，双方设备还未建立好友关系，{}：{}", device.getId(), imei2);
            //发送删除设备碰碰好友的指令
            delIme1PpFriend(device.getId(), imei2, device2.getPhone());
            return;
        }
        Map<String, Map<String, Object>> ppFriendOuterMapA = device.getPpFriends();
        Map<String, Map<String, Object>> ppFriendOuterMapB = device2.getPpFriends();
        boolean isAContainB = !MapUtils.isEmpty(ppFriendOuterMapA) && !MapUtils.isEmpty(ppFriendOuterMapA.get(imei2));
        boolean isBContainA = !MapUtils.isEmpty(ppFriendOuterMapB) && !MapUtils.isEmpty(ppFriendOuterMapB.get(device.getId()));
        if (!(isAContainB && isBContainA)) {
            if (!isAContainB) {
                //发送删除设备碰碰好友的指令
                delIme1PpFriend(device.getId(), imei2, device2.getPhone());
            }
            log.error("设备消息发送失败，双方设备还未完全建立好友关系，{}：{}", device.getId(), imei2);
            return;
        }
        //查询群聊消息，用户未绑定任何联系人，碰碰交友的发送语音不太好判断会员信息
        ChatGroup chatGroup = chatGroupService.getChatGroupByDevice(device);
        if (chatGroup == null) {
            log.warn("imei:{} 的找不到对应群组,设备未绑定用户,暂时无法使用碰碰交友, return", device.getId());
            return;
        }
        Set<String> openids = chatGroup.getMemberMap().keySet();
        //检查成员中有没有会员
        List<VipMember> vipMembers = vipMemberService.queryVipMemberByOpenids(openids);
        int todayVoiceTimes = 0;
        Calendar calendar = Calendar.getInstance();
        String todayKey = device.getId() + DateUtil.dateToStr(calendar.getTime(), DateUtil.FORMAT_5);
        JSONObject jsonData = checkVoiceTimeUpLimit(calendar, device.getId(), todayKey, todayVoiceTimes);
        //检查双方是否都有对方的好友信息
        String dscgId = device.getId().compareTo(imei2) > 0 ? device.getId() + "&" + imei2 : imei2 + "&" + device.getId();
        DeviceSingleChatGroup deviceSingleChatGroup = deviceSingleChatGroupService.findById(dscgId);
        if (ObjectUtils.isEmpty(deviceSingleChatGroup)) {
            //不存在则创建设备单聊群
            deviceSingleChatGroupService.saveDeviceSingleGroup(device.getId(), imei2);
        }
        if ("upPpFriendVoice".equals(updateType)) {
            DeviceSingleChatGroupMessage deviceSingleChatGroupMessage = new DeviceSingleChatGroupMessage();
            deviceSingleChatGroupMessage.setGroupid(dscgId);
            deviceSingleChatGroupMessage.setSendType(2);
            deviceSingleChatGroupMessage.setAvator(baseBucketTool.getDOMAIN() + BaseBucketTool.DEVICE_COMMON_PATH
                    + (StringUtil.isBlank(device.getImage()) ? Const.DEFAULT_JPG : device.getImage()));
            deviceSingleChatGroupMessage.setSender(device.getId());
            deviceSingleChatGroupMessage.setReceiver(imei2);
            //设置记录是否超过限制
            if (!CollectionUtils.isEmpty(vipMembers) || jsonData.getBooleanValue("isAllow")) {
                //是会员或未超过限制
                deviceSingleChatGroupMessage.setIsExceedLimitRecord(1);
            } else {
                //超过限制的
                deviceSingleChatGroupMessage.setIsExceedLimitRecord(0);
            }
            if (ObjectUtils.isNotEmpty(message.get("msgTs"))) {
                deviceSingleChatGroupMessage.setChatReportTime(DateUtil.stampToDateStr(message.getLong("msgTs") * 1000, DateUtil.FORMAT_1));
            } else {
                deviceSingleChatGroupMessage.setChatReportTime(DateUtil.stampToDateStr(System.currentTimeMillis(), DateUtil.FORMAT_1));
            }
            deviceSingleChatGroupMessage.setMessageType(3);
            deviceSingleChatGroupMessage.setContent(message.getString("voicePath"));
            deviceSingleChatGroupMessage.setSize(message.getLong("size"));
            deviceSingleChatGroupMessage.setDuration(message.getDouble("duration"));
            //转发消息到对应设备
            if (!CollectionUtils.isEmpty(vipMembers) || jsonData.getBooleanValue("isAllow")) {
                JSONObject kafkaJson = new JSONObject();
                kafkaJson.put("id", device.getId().substring(4, 14));
                kafkaJson.put("currentTime", ObjectUtils.isNotEmpty(message.get("msgTs")) ? message.get("msgTs").toString() : "" + System.currentTimeMillis() / 1000);
                kafkaJson.put("voicePath", "/" + BaseBucketTool.MESSAGE_COMMON_PATH + message.get("voicePath"));
                kafkaService.emitDeviceDemand(imei2, ConstKafka.DEVICE_TYPE_PP_FRIEND_SEND_VOICE, kafkaJson, null);
            }
            //保存设备消息
            deviceSingleChatGroupMessageService.add(deviceSingleChatGroupMessage);
            //不是会员且当天没有到微聊消息上限的，将发送次数加1
            if (CollectionUtils.isEmpty(vipMembers) && jsonData.getBooleanValue("isAllow")) {
                todayVoiceTimes = jsonData.getIntValue("todayVoiceTimes");
                log.info("今日已经下发记录次数:{}", todayVoiceTimes);
                todayVoiceTimes++;
                redisFilterService.setex(todayKey, 24 * 60 * 60, String.valueOf(todayVoiceTimes));
            } else {
                // 推送提示消息给绑定的用户
                String content = "设备的碰碰交友消息推送次数已达上限，建议您充值成为会员解锁推送限制";


                // 遍历所有绑定用户，发送推送消息
                for (String openid : openids) {
                    pushAccountMessage(device, "weChatMsg", "API.pushTitle.ppfriend.limit", openid, content, true);
                }
            }

        }
    }

    /**
     * @param [device, imei2, device2]
     * @return void
     * @description 删除设备的碰碰好友
     * @author cwj
     * @date @time 2025/7/7 18:02
     **/
    private void delIme1PpFriend(String imei1, String imei2, String phone2) {
        JSONObject kafkaJson = new JSONObject();
        kafkaJson.put("id", imei2.substring(4, 14));
        kafkaJson.put("phone", phone2);
        kafkaService.emitDeviceDemand(imei1, ConstKafka.DEVICE_TYPE_PP_FRIEND_DEL, kafkaJson, null);
    }

    /**
     * @param [device, message]
     * @return void
     * @description 删除碰碰好友
     * @author cwj
     * @date @time 2025/7/7 10:57
     **/
    private void type_ppFriendDel(Device device, JSONObject message) {
        //要推送的内容
        String imei2 = ObjectUtils.isNotEmpty(message.get("otherImei")) ? message.getString("otherImei") : null;
        String shortId2 = message.getString("id");
        if (StringUtils.isEmpty(imei2)) {
            imei2 = redisClusterService.get("SHORT:" + shortId2);
        }
        if (StringUtils.isEmpty(imei2)) {
            log.info("设备：{}发送失败，待接收消息的设备:{}未在系统中注册或已删除", device.getId(), shortId2);
            if (!MapUtils.isEmpty(device.getPpFriends()) && !MapUtils.isEmpty(device.getPpFriends().get(imei2))) {
                //去掉好友列表中的好友信息
                updateDevicePpFiend(device, imei2);
            }
        } else {
            Device device2 = deviceService.findById(imei2);
            if (ObjectUtils.isEmpty(device2)) {
                log.error("设备：{}发送失败，待接收消息的设备：{}未在系统中注册或已删除", device.getId(), imei2);
                //删除当前设备中关联信息
                if (!MapUtils.isEmpty(device.getPpFriends()) && !MapUtils.isEmpty(device.getPpFriends().get(imei2))) {
                    updateDevicePpFiend(device, imei2);
                }
            } else {
                Map<String, Map<String, Object>> ppFriendOuterMapA = device.getPpFriends();
                Map<String, Map<String, Object>> ppFriendOuterMapB = device2.getPpFriends();
                if (MapUtils.isEmpty(ppFriendOuterMapA) || MapUtils.isEmpty(ppFriendOuterMapB)) {
                    log.error("设备消息发送失败，双方设备还未建立好友关系，{}：{}", device.getId(), imei2);
                    if (!MapUtils.isEmpty(ppFriendOuterMapA)) {
                        if (!MapUtils.isEmpty(ppFriendOuterMapA.get(imei2))) {
                            //删除PP交友的好友信息，删除对应群组信息
                            ppFriendOuterMapA.remove(imei2);
                            delPpFriendRelation(device.getId(), imei2, ppFriendOuterMapA, device.getId());
                        }
                    } else if (!MapUtils.isEmpty(ppFriendOuterMapB)) {
                        if (!MapUtils.isEmpty(ppFriendOuterMapB.get(device.getId()))) {
                            //删除PP交友的好友信息，删除对应群组信息
                            ppFriendOuterMapB.remove(device.getId());
                            delPpFriendRelation(device.getId(), imei2, ppFriendOuterMapB, imei2);
                            //发送删除碰碰好友的指令
                            delImei2PpFriendCommand(device.getId(), device.getPhone(), imei2);
                        }
                    }
                } else {
                    boolean isAContainB = !MapUtils.isEmpty(ppFriendOuterMapA) && !MapUtils.isEmpty(ppFriendOuterMapA.get(imei2));
                    boolean isBContainA = !MapUtils.isEmpty(ppFriendOuterMapB) && !MapUtils.isEmpty(ppFriendOuterMapB.get(device.getId()));
                    if (isAContainB && isBContainA) {
                        ppFriendOuterMapA.remove(imei2);
                        delPpFriendRelation(device.getId(), imei2, ppFriendOuterMapA, device.getId());
                        ppFriendOuterMapB.remove(device.getId());
                        delPpFriendRelation(device.getId(), imei2, ppFriendOuterMapB, imei2);
                        //发送删除碰碰好友的指令
                        delImei2PpFriendCommand(device.getId(), device.getPhone(), imei2);
                    } else if (isAContainB) {
                        ppFriendOuterMapA.remove(imei2);
                        delPpFriendRelation(device.getId(), imei2, ppFriendOuterMapA, device.getId());
                    } else if (isBContainA) {
                        ppFriendOuterMapB.remove(device.getId());
                        delPpFriendRelation(device.getId(), imei2, ppFriendOuterMapB, imei2);
                        //发送删除碰碰好友的指令
                        delImei2PpFriendCommand(device.getId(), device.getPhone(), imei2);
                    } else {
                        log.error("两台设备都不存在好友关系，无法删除");
                    }
                }
            }
        }
    }

    /**
     * @param [device, message]
     * @return void
     * @description PP好友，补充好友信息，设备好友列表发现没有这个好友记录，却收到这个ID发送过来的数据，补全好友信息
     * @author cwj
     * @date @time 2025/7/7 15:14
     **/
    private void type_ppFriendGet(Device device, JSONObject message) {
        //要推送的内容
        String imei2 = ObjectUtils.isNotEmpty(message.get("otherImei")) ? message.getString("otherImei") : null;
        String shortId2 = message.getString("id");
        if (StringUtils.isEmpty(imei2)) {
            imei2 = redisClusterService.get("SHORT:" + shortId2);
        }
        if (StringUtils.isEmpty(imei2)) {
            log.info("设备：{}发送失败，待接收消息的设备:{}未在系统中注册或已删除", device.getId(), shortId2);
            return;
        }
        Device device2 = deviceService.findById(imei2);
        if (ObjectUtils.isEmpty(device2)) {
            log.error("设备：{}发送失败，待接收消息的设备：{}未在系统中注册或已删除", device.getId(), imei2);
            return;
        }
        Map<String, Map<String, Object>> ppFriendOuterMapA = device.getPpFriends();
        Map<String, Map<String, Object>> ppFriendOuterMapB = device2.getPpFriends();
        boolean isAContainB = !MapUtils.isEmpty(ppFriendOuterMapA) && !MapUtils.isEmpty(ppFriendOuterMapA.get(imei2));
        boolean isBContainA = !MapUtils.isEmpty(ppFriendOuterMapB) && !MapUtils.isEmpty(ppFriendOuterMapB.get(device.getId()));
        if (!isAContainB) {
            Map<String, Object> ppFriendInnerMapA = new HashMap<>();
            ppFriendInnerMapA.put("t", System.currentTimeMillis() / 1000);
            //补全好友列表
            ppFriendOuterMapA.put(imei2, ppFriendInnerMapA);
            Update update = new Update();
            update.set("pf", ppFriendOuterMapA);
            deviceService.updateById(device.getId(), update);
        }
        if (!isBContainA) {
            //补全好友列表
            Map<String, Object> ppFriendInnerMapB = new HashMap<>();
            ppFriendInnerMapB.put("t", System.currentTimeMillis() / 1000);
            ppFriendOuterMapB.put(device.getId(), ppFriendInnerMapB);
            Update update = new Update();
            update.set("pf", ppFriendOuterMapB);
            deviceService.updateById(device.getId(), update);
        }
        //创建设备的单聊群组，不存在则创建群组
        deviceSingleChatGroupService.createDeviceSingleGroup(device.getId(), imei2);
        JSONObject kafkaJson = new JSONObject();
        assmbleKafkaJson(kafkaJson, imei2, device2);
        kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_PP_FRIEND_RESP, kafkaJson, null);
        //下发好友列表
        assmbleKafkaJson(kafkaJson, device.getId(), device);
        kafkaService.emitDeviceDemand(imei2, ConstKafka.DEVICE_TYPE_PP_FRIEND_RESP, kafkaJson, null);
    }

    /**
     * @param [device, imei2]
     * @return void
     * @description
     * @author cwj
     * @date @time 2025/7/7 15:26
     **/
    private void updateDevicePpFiend(Device device, String imei2) {
        Update deviceUpdate = new Update();
        device.getPpFriends().remove(imei2);
        deviceUpdate.set("pf", device.getPpFriends());
        deviceService.updateById(device.getId(), deviceUpdate);
    }

    /**
     * @param [device, imei2]
     * @return void
     * @description 删除碰碰好友
     * @author cwj
     * @date @time 2025/7/7 11:52
     **/
    private void delImei2PpFriendCommand(String imei1, String phone1, String imei2) {
        JSONObject kafkaJson = new JSONObject();
        kafkaJson.put("id", imei1.substring(4, 14));
        kafkaJson.put("phone", phone1);
        kafkaService.emitDeviceDemand(imei2, ConstKafka.DEVICE_TYPE_PP_FRIEND_DEL, kafkaJson, null);
    }


    /**
     * @param [device, ppFriendOuterMapA, imei2, deviceUpdate]
     * @return void
     * @description 删除设备PP好友，删除关联PP好友群组
     * @author cwj
     * @date @time 2025/7/7 11:36
     **/
    private void delPpFriendRelation(String imei1, String imei2, Map<String, Map<String, Object>> ppFriendOuterMap, String updateImei) {
        Update deviceUpdate = new Update();
        deviceUpdate.set("pf", ppFriendOuterMap);
        deviceService.updateById(updateImei, deviceUpdate);
        deviceSingleChatGroupService.delDataByImei(imei1, imei2);
    }

    /**
     * @param [device, message]
     * @return void
     * @description 设备上报获微聊联系人的信息
     * @author cwj
     * @date @time 2025/7/10 18:28
     **/
    private void type_AppContactGet(Device device, JSONObject message) {
        String imei = device.getId();
        // String phone = message.getString("phone");
        String openId = message.getString("openId");
        //Account account = accountService.queryAccountByPhone(phone,86);
        Account account = accountService.findById(openId);
        if (ObjectUtils.isNotEmpty(account)) {
            WechatContact wechatContact = wechatContactService.queryByImeOpenId(imei, account.getId());
            if (ObjectUtils.isNotEmpty(wechatContact)) {
                //下发微聊视频联系人信息
                JSONObject kafkaJson = new JSONObject();
                kafkaJson.put("name", wechatContact.getName());
                //kafkaJson.put("phone",account.getPhone());
                kafkaJson.put("openId", account.getId());
                //没有上传图像则用旧图像，旧图像不存在或是默认图像则没有图像，暂时不发头像，没搞清楚协议中图像数据的发送的编码
                if (StringUtil.isNotBlank(account.getImage()) && !account.getImage().contains("default.jpg")) {
                    if (!"ASR".equals(device.getPlatform())) {
                        kafkaJson.put("avator", "/" + BaseBucketTool.ACCOUNT_COMMON_PATH + account.getImage());
                    }
                }
                log.info("新版协议添加微聊联系人及视频联系人的指令，指令数据为：{}", kafkaJson);
                //发送视频微聊联系人指令
                kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_ADD_NEW_CONTACT, kafkaJson, account.getId());
            }
        }
    }

    /**
     * @param [device, message]
     * @return void
     * @description 解析新版微聊消息数据
     * @author cwj
     * @date @time 2025/6/29 18:05
     **/
    private void type_newChatmsg(Device device, JSONObject message) {
        //要推送的内容
        JSONObject subPushJson = new JSONObject();
        String updateType = message.getString("type");
        String singleGroupChatFlag = message.getString("x");
        //String phone = message.getString("phone");
        String openId = message.getString("openId");
        //保存单聊数据，推送单聊消息
        if ("0".equals(singleGroupChatFlag)) {
            //发送删除
            JSONObject kafkaJson = new JSONObject();
            //kafkaJson.put("phone",phone);
            kafkaJson.put("openId", openId);
            //根据号码查询用户信息
            // Account account = accountService.queryAccountByPhone(phone,86);
            Account account = accountService.findById(openId);
            if (ObjectUtils.isEmpty(account)) {
                //下发删除指令
                kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_DEL_NEW_CONTACT, kafkaJson, account.getId());
                //log.error("imei:{}上报的设备电话：{}找不到用户，用户不存在或已经注销",device.getId(),phone);
                log.error("imei:{}上报的设备电话：{}找不到用户，用户不存在或已经注销", device.getId(), openId);
                return;
            }
            // WechatContact wechatContact = wechatContactService.queryByImePhone(device.getId(),phone);
            WechatContact wechatContact = wechatContactService.queryByImeOpenId(device.getId(), openId);
            //检查电话是否在聊天列表中，不在则下发删除指令
            if (ObjectUtils.isEmpty(wechatContact)) {
                //下发删除指令
                kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_DEL_NEW_CONTACT, kafkaJson, account.getId());
                // log.info("imei:{}上报的设备电话：{}已经不在微聊联系人列表中",device.getId(),phone);
                log.info("imei:{}上报的设备电话：{}已经不在微聊联系人列表中", device.getId(), openId);
                return;
            }
            SingleChatGroup singleChatGroup = singleChatGroupService.findById(account.getId() + "&" + device.getId());
            if (ObjectUtils.isEmpty(singleChatGroup)) {
                //log.error("电话号码为：{}的账户和设备：{}的单聊关系没有建立或已解绑",phone,device.getId());
                log.error("电话号码为：{}的账户和设备：{}的单聊关系没有建立或已解绑", openId, device.getId());
                return;
            }
            //解析单聊数据，推送单聊消息
            newSingleChatDataAnalysis(device, message, account, singleChatGroup, updateType, subPushJson);
        } else {
            //保存群聊数据，推送群聊消息
            ChatGroup chatGroup = chatGroupService.getChatGroupByDevice(device);
            if (chatGroup == null) {
                log.warn("imei:{} 的找不到对应群组, return", device.getId());
                return;
            }
            //新版协议群聊数据解析处理
            newGroupChatDataAnalysis(device, message, chatGroup, updateType, subPushJson);
        }
    }


    /**
     * @param [device, message, account, singleChatGroup, updateType, subPushJson]
     * @return void
     * @description 新版协议单聊数据解析处理
     * @author cwj
     * @date @time 2025/6/30 15:01
     **/
    private void newSingleChatDataAnalysis(Device device, JSONObject message, Account account, SingleChatGroup singleChatGroup, String updateType, JSONObject subPushJson) {
        //查询用户是否为VIP
        VipMember vipMember = vipMemberService.queryVipMemberByOpenid(account.getId());
        int todayVoiceTimes = 0;
        Calendar calendar = Calendar.getInstance();
        String todayKey = device.getId() + DateUtil.dateToStr(calendar.getTime(), DateUtil.FORMAT_5);
        JSONObject jsonData = checkVoiceTimeUpLimit(calendar, device.getId(), todayKey, todayVoiceTimes);
        String singleGroupId = singleChatGroup.getId();
        if ("upNewChatVoice".equals(updateType) || "upNewChatImage".equals(updateType) || "upNewChatEmoji".equals(updateType)) {
            SingleChatGroupMessage singleChatGroupMessage = new SingleChatGroupMessage();
            singleChatGroupMessage.setGroupid(singleGroupId);
            singleChatGroupMessage.setSendType(2);
            singleChatGroupMessage.setAvator(baseBucketTool.getDOMAIN() + BaseBucketTool.DEVICE_COMMON_PATH
                    + (StringUtil.isBlank(device.getImage()) ? Const.DEFAULT_JPG : device.getImage()));
            singleChatGroupMessage.setSender(device.getId());
            //设置记录是否超过限制
            if (!ObjectUtils.isEmpty(vipMember) || jsonData.getBooleanValue("isAllow")) {
                singleChatGroupMessage.setIsExceedLimitRecord(1);
            } else {
                //超过限制的
                singleChatGroupMessage.setIsExceedLimitRecord(0);
            }
            if (ObjectUtils.isNotEmpty(message.get("msgTs"))) {
                singleChatGroupMessage.setChatReportTime(DateUtil.stampToDateStr(message.getLong("msgTs") * 1000, DateUtil.FORMAT_1));
            } else {
                singleChatGroupMessage.setChatReportTime(DateUtil.stampToDateStr(System.currentTimeMillis(), DateUtil.FORMAT_1));
            }
            // 1:文字,2:图片,3:语音.4:表情包
            switch (updateType) {
                case "upNewChatVoice": {
                    //语音
                    singleChatGroupMessage.setMessageType(3);
                    singleChatGroupMessage.setContent(message.getString("voicePath"));
                    singleChatGroupMessage.setSize(message.getLong("size"));
                    singleChatGroupMessage.setDuration(message.getDouble("duration"));
                    subPushJson.put("content", baseBucketTool.getDOMAIN() + BaseBucketTool.MESSAGE_COMMON_PATH + message.get("voicePath"));
                    subPushJson.put("duration", message.get("duration"));
                }
                break;
                case "upNewChatImage": {
                    //图片
                    singleChatGroupMessage.setMessageType(2);
                    singleChatGroupMessage.setContent(message.getString("imagePath"));
                    singleChatGroupMessage.setSize(message.getLong("size"));
                    singleChatGroupMessage.setDuration(0.0);
                    subPushJson.put("content", baseBucketTool.getDOMAIN() + BaseBucketTool.MESSAGE_COMMON_PATH + message.get("imagePath"));
                }
                break;
                case "upNewChatEmoji": {
                    //图片
                    singleChatGroupMessage.setMessageType(4);
                    singleChatGroupMessage.setContent(message.getString("emoji"));
                    singleChatGroupMessage.setBqId(message.getString("emoji"));
                    singleChatGroupMessage.setSize(message.getLong("size"));
                    singleChatGroupMessage.setDuration(0.0);
                    subPushJson.put("content", message.getString("emoji"));
                }
                break;
            }
            //内容安全审查
            if (!checkContentSecurity(device.getId(), updateType, subPushJson.getString("content"), singleGroupId, singleChatGroupMessage.getId())) {
                sensitivityFileSingleMessageService.saveData(singleGroupId, singleChatGroupMessage);
                //检测敏感词====
                log.error("上报信息中包含敏感信息，不能推送此消息给APP用户，含有敏感信息的文件链接为：{}", subPushJson.get("content"));
                return;
            }
            //保存微聊信息
            singleChatGroupMessageService.add(singleChatGroupMessage);
            //更新单聊未读的消息
            Update updateSingleChatGroup = new Update();
            //更新未读消息，不管是不是会员，没超过限制就加上去（防止由会员变成非会员时，未读数据有问题）
            if (ObjectUtils.isNotEmpty(vipMember) || (ObjectUtils.isEmpty(vipMember) && jsonData.getBooleanValue("isAllow"))) {
                //不是会员且没有超过每天的微聊条数限制
                updateSingleChatGroup.set("noVipBage", ObjectUtils.isEmpty(singleChatGroup.getNoVipBage()) ? 1 : singleChatGroup.getNoVipBage() + 1);
            }
            updateSingleChatGroup.set("bage", ObjectUtils.isEmpty(singleChatGroup.getBage()) ? 1 : singleChatGroup.getBage() + 1);
            singleChatGroupService.updateById(singleChatGroup.getId(), updateSingleChatGroup);
            subPushJson.put("id", singleChatGroup.getId());
            subPushJson.put("senderID", device.getId());
            subPushJson.put("senderType", 2);
            subPushJson.put("type", singleChatGroupMessage.getMessageType());
            subPushJson.put("timestamp", DateUtil.nowTimestampToLong());
            //发送kafka
            subPushJson.put("msgTitle", MessageUtils.getLocale(device.getLocale(), "API.pushTitle.newChatMsg"));
            subPushJson.put("timestamp", DateUtil.nowTimestampToDouble());
            //单聊消息
            subPushJson.put("groupType", 1);
            JSONObject pushData = new JSONObject();
            pushData.put("type", ConstKafka.SINGLE_CHAT_GROUP_MESSAGE);
            pushData.put("groupid", singleGroupId);
            //告知APP是新版还是旧版协议
            pushData.put("isNewWechatProtocol", device.getIsNewWechatProtocol());
            pushData.put("message", subPushJson);
            if (!ObjectUtils.isEmpty(vipMember)) {
                kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_1, singleGroupId, pushData);
            } else {
                //检查是否允许推送记录
                if (jsonData.getBooleanValue("isAllow")) {
                    todayVoiceTimes = jsonData.getIntValue("todayVoiceTimes");
                    log.info("今日已经下发记录次数:{}", todayVoiceTimes);
                    kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_1, singleGroupId, pushData);
                    todayVoiceTimes++;
                    redisFilterService.setex(todayKey, 24 * 60 * 60, String.valueOf(todayVoiceTimes));
                } else {
                    //推送消息告诉用户，因为没有购买会员，今天已经到达推送消息的上限，设备消息无法再推送过来，提醒家长，群聊消息只需一个家长购买会员即可享受不受限制的设备微聊信息的推送
                    pushAccountMessage(device, "weChatMsg", "API.pushTitle.wechat.limit", null, "设备的微聊记录推送次数已达上限，建议您充值成为会员解锁推送限制", true);
                }
            }
        }
    }

    /**
     * @param [device, message, chatGroup, updateType, subPushJson]
     * @return void
     * @description 新版协议群聊数据解析处理
     * @author cwj
     * @date @time 2025/6/30 10:56
     **/
    private void newGroupChatDataAnalysis(Device device, JSONObject message, ChatGroup chatGroup, String updateType, JSONObject subPushJson) {
        Set<String> openids = chatGroup.getMemberMap().keySet();
        //检查成员中有没有会员
        List<VipMember> vipMembers = vipMemberService.queryVipMemberByOpenids(openids);
        int todayVoiceTimes = 0;
        Calendar calendar = Calendar.getInstance();
        String todayKey = device.getId() + DateUtil.dateToStr(calendar.getTime(), DateUtil.FORMAT_5);
        JSONObject jsonData = checkVoiceTimeUpLimit(calendar, device.getId(), todayKey, todayVoiceTimes);
        //区分单聊还是群聊
        String groupId = chatGroup.getId();
        if ("upNewChatVoice".equals(updateType) || "upNewChatImage".equals(updateType) || "upNewChatEmoji".equals(updateType)) {
            //表拥有的群组 都插入一条聊天消息
            ChatGroupMessage chatGroupMessage = new ChatGroupMessage();
            ObjectId gid = new ObjectId(groupId);
            chatGroupMessage.setGroupid(gid);
            //1:用户 2:设备
            chatGroupMessage.setSendType(2);
            chatGroupMessage.setAvator(baseBucketTool.getDOMAIN() + BaseBucketTool.DEVICE_COMMON_PATH
                    + (StringUtil.isBlank(device.getImage()) ? Const.DEFAULT_JPG : device.getImage()));
            chatGroupMessage.setSender(device.getId());
            if (ObjectUtils.isNotEmpty(message.get("msgTs"))) {
                chatGroupMessage.setChatReportTime(DateUtil.stampToDateStr(message.getLong("msgTs") * 1000, DateUtil.FORMAT_1));
            } else {
                chatGroupMessage.setChatReportTime(DateUtil.stampToDateStr(System.currentTimeMillis(), DateUtil.FORMAT_1));
            }
            //设置记录是否超过限制
            if (!CollectionUtils.isEmpty(vipMembers) || jsonData.getBooleanValue("isAllow")) {
                chatGroupMessage.setIsExceedLimitRecord(1);
            } else {
                chatGroupMessage.setIsExceedLimitRecord(0);
            }
            // 1:文字,2:图片,3:语音.4:表情包
            switch (updateType) {
                case "upNewChatVoice": {
                    //语音
                    chatGroupMessage.setMessageType(3);
                    chatGroupMessage.setContent(message.getString("voicePath"));
                    chatGroupMessage.setSize(message.getLong("size"));
                    chatGroupMessage.setDuration(message.getDouble("duration"));
                    subPushJson.put("content", baseBucketTool.getDOMAIN() + BaseBucketTool.MESSAGE_COMMON_PATH + message.get("voicePath"));
                    subPushJson.put("duration", message.get("duration"));
                }
                break;
                case "upNewChatImage": {
                    //图片
                    chatGroupMessage.setMessageType(2);
                    chatGroupMessage.setContent(message.getString("imagePath"));
                    chatGroupMessage.setSize(message.getLong("size"));
                    chatGroupMessage.setDuration(0.0);
                    subPushJson.put("content", baseBucketTool.getDOMAIN() + BaseBucketTool.MESSAGE_COMMON_PATH + message.get("imagePath"));
                }
                break;
                case "upNewChatEmoji": {
                    //图片
                    chatGroupMessage.setMessageType(4);
                    chatGroupMessage.setContent(message.getString("emoji"));
                    chatGroupMessage.setBqId(message.getString("emoji"));
                    chatGroupMessage.setSize(message.getLong("size"));
                    chatGroupMessage.setDuration(0.0);
                    subPushJson.put("content", message.getString("emoji"));
                }
                break;
            }
            //内容安全审查
            if (!checkContentSecurity(device.getId(), updateType, subPushJson.getString("content"), groupId, chatGroupMessage.getId())) {
                //保存群聊敏感语音信息记录
                sensitivityFileGroupMessageService.saveData(groupId, chatGroupMessage);
                //检测敏感词====
                log.error("上报信息中包含敏感信息，不能推送此消息给APP用户，含有敏感信息的文件链接为：{}", subPushJson.get("content"));
                return;
            }
            //保存语音微聊信息
            chatGroupMessageService.add(chatGroupMessage);
            if (CollectionUtil.isNotEmpty(chatGroup.getMemberMap())) {
                //给所有的绑定这个群组的用户 加未读消息 +1
                FindIterable<Document> accountDocs = accountService.listDocByIds(chatGroup.getMemberMap().keySet());
                for (Document doc : accountDocs) {
                    Update updateAccount = new Update();
                    if (!CollectionUtils.isEmpty(vipMembers) || (CollectionUtils.isEmpty(vipMembers) && jsonData.getBooleanValue("isAllow"))) {
                        //非会员且每天未超过限制次数时的未读记录次数
                        String field = Constant.NO_VIP_UNREAD_TIME_PREFIX + groupId;
                        updateAccount.set(field, doc.getInteger(field) == null ? 1 : doc.getInteger(field) + 1);
                    }
                    updateAccount.set(groupId, doc.getInteger(groupId) == null ? 1 : doc.getInteger(groupId) + 1);
                    updateAccount.set("badge", doc.getInteger("badge") == null ? 1 : doc.getInteger("badge") + 1);
                    accountService.updateById(doc.getString("_id"), updateAccount);
                }
            }
            subPushJson.put("id", chatGroupMessage.getId());
            subPushJson.put("senderID", device.getId());
            subPushJson.put("senderType", 2);
            subPushJson.put("type", chatGroupMessage.getMessageType());
            subPushJson.put("timestamp", DateUtil.nowTimestampToLong());
        }
        //发送kafka
        subPushJson.put("msgTitle", MessageUtils.getLocale(device.getLocale(), "API.pushTitle.newChatMsg"));
        subPushJson.put("timestamp", DateUtil.nowTimestampToDouble());
        JSONObject pushData = new JSONObject();
        pushData.put("type", ConstKafka.CHAT_GROUP_MESSAGE);
        pushData.put("groupid", groupId);
        pushData.put("isNewWechatProtocol", device.getIsNewWechatProtocol());
        pushData.put("message", subPushJson);
        //kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_1, groupId, pushData);
        //控制消息推送，检查imei绑定的用户有没有会员
        if (CollectionUtil.isNotEmpty(chatGroup.getMemberMap())) {
            if (!CollectionUtils.isEmpty(vipMembers)) {
                kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_1, groupId, pushData);
            } else {
                //检查是否允许推送记录
                if (jsonData.getBooleanValue("isAllow")) {
                    todayVoiceTimes = jsonData.getIntValue("todayVoiceTimes");
                    log.info("今日已经下发记录次数:{}", todayVoiceTimes);
                    kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_1, groupId, pushData);
                    todayVoiceTimes++;
                    redisFilterService.setex(todayKey, 24 * 60 * 60, String.valueOf(todayVoiceTimes));
                } else {
                    //推送消息告诉用户，因为没有购买会员，今天已经到达推送消息的上限，设备消息无法再推送过来，提醒家长，群聊消息只需一个家长购买会员即可享受不受限制的设备微聊信息的推送
                    pushAccountMessage(device, "weChatMsg", "API.pushTitle.wechat.limit", null, "设备的微聊记录推送次数已达上限，建议您充值成为会员解锁推送限制", true);
                }
            }
        }
    }

    /**
     * @param [devId, updateType, content, groupId, msgId]
     * @return boolean
     * @description 语音及图片的安全检查
     * @author cwj
     * @date @time 2025/7/10 9:32
     **/
    private boolean checkContentSecurity(String devId, String updateType, String content, String groupId, String msgId) {
        if ("upNewChatVoice".equalsIgnoreCase(updateType)) {
            //检测敏感词-语音====
            if (!contentSecurityUtil.chatAudioAudit(content, devId, groupId, msgId)) {
                log.error("内容安全语音审核，检测到敏感语音,出现敏感语音的群组为：{}", groupId);
                return false;
            }
        } else if ("upNewChatImage".equalsIgnoreCase(updateType)) {
            //检测到敏感图片
            if (!ContentSecurityUtil.imageAudit(content, devId)) {
                log.info("内容安全图片审核，检测到敏感图片,出现敏感图片的群组为：{}", groupId);
                return false;
            }
        }
        return true;
    }


    /**
     * @param [calendar, imei, todayKey, todayVoiceTimes]
     * @return boolean
     * @description 检查非会员的限制
     * @author cwj
     * @date @time 2025/1/8 15:53
     **/
    public JSONObject checkVoiceTimeUpLimit(Calendar calendar, String imei, String todayKey, int todayVoiceTimes) {
        boolean isAllow;

        //默认值
        int voiceTimesUpLimit = 10;
        //检查imei今天推送消息有没有到上限，如果到了上限则不允许发送
        String deviceSendVoiceTimesUpLimitKey = Constant.DEVICE_SEND_VOICE_TIMES_UP_LIMIT_REDIS_KEY;  //15
        String sendVoiceTimesUpLimit = redisFilterService.get(deviceSendVoiceTimesUpLimitKey);
        if (StringUtils.isEmpty(sendVoiceTimesUpLimit)) {
            //查询限制表，无数据说明没设置，没设置就用默认的
            SystemLimit systemLimit = systemLimitService.querySystemLimit();
            if (!ObjectUtils.isEmpty(systemLimit)) {
                //设置限制次数
                voiceTimesUpLimit = systemLimit.getDeviceSendVoiceTimesUpLimit();
                redisFilterService.set(deviceSendVoiceTimesUpLimitKey, String.valueOf(voiceTimesUpLimit));
            }
        } else {
            voiceTimesUpLimit = Integer.parseInt(sendVoiceTimesUpLimit);
        }
        calendar.add(Calendar.DATE, -1);
        Date yesterdayDate = calendar.getTime();
        String yesterday = imei + DateUtil.dateToStr(yesterdayDate, DateUtil.FORMAT_5);
        if (!StringUtils.isEmpty(redisFilterService.get(yesterday))) {
            redisFilterService.del(yesterday);
        }
        String voiceTimesStr = redisFilterService.get(todayKey);
        if (StringUtils.isEmpty(voiceTimesStr)) {
            isAllow = true;
        } else {
            todayVoiceTimes = Integer.valueOf(voiceTimesStr);
            if (todayVoiceTimes < voiceTimesUpLimit) {
                isAllow = true;
            } else {
                isAllow = false;
            }
        }
        JSONObject data = new JSONObject();
        data.put("isAllow", isAllow);
        data.put("todayVoiceTimes", todayVoiceTimes);
        return data;
    }

    /**
     * 碰碰交友 上传聊天消息
     *
     * @param device
     * @param message
     * @time: 2022/1/17 17:46
     * @author: Weichao
     * @version 1.0
     */
    private void type_upPPMsg(Device device, JSONObject message) {
        //发送设备的 碰碰交友聊天群
        PpChatGroup ppChatGroup = ppChatGroupService.getByImei(device.getId());
        if (ppChatGroup == null) {
            log.error("PpChatGroup is null. message={}", message);
            return;
        }
        //下发给手表的指令的数据
        JSONObject kafkaData = new JSONObject();
        String targetimei = message.getString("pp_imei");
        String messagePath = "";
        if (message.containsKey("voicePath")) {
            messagePath = message.getString("voicePath");
        } else if (message.containsKey("imagePath")) {
            messagePath = message.getString("imagePath");
        } else {
            messagePath = message.getString("url");
        }
        //messagePath 取其文件名
        String[] msgPathSplit = messagePath.split("/");
        messagePath = msgPathSplit[msgPathSplit.length - 1];
        String msgurl = "";
        String platform = device.getPlatform();
        String version = device.getVersion();
        if (StringUtil.isBlank(platform) || ("ASR".equals(platform) && StringUtil.isNumeric(version)
                && Integer.parseInt(version) < 20201110)) {
            msgurl = String.format("http://%s/getway/download/%s", domainName, messagePath);
        } else {
            msgurl = baseBucketTool.getDOMAIN() + BaseBucketTool.PPMESSAGE_COMMON_PATH + messagePath;
        }
        kafkaData.put("messageurl", msgurl);

        if (message.containsKey("size")) {
            kafkaData.put("size", message.get("size"));
        }
        kafkaData.put("vender", message.getString("vender"));
        kafkaData.put("type", "downPPmessage");
        //发送给对方的好友时, 应该填写自身的imei
        kafkaData.put("friendimei", device.getId());

        //碰碰交友消息data 保存到数据库的
        JSONObject ppChatMsgData = new JSONObject();

        ppChatMsgData.put("pcgid", ppChatGroup.getId());
        ppChatMsgData.put("sender", device.getId());
        ppChatMsgData.put("acceptor", targetimei);
        ppChatMsgData.put("content", messagePath);

        PpChatGroupMessage ppChatGroupMessage = ppChatGroupMessageService.add(ppChatMsgData);
        //消息ID
        kafkaData.put("id", ppChatGroupMessage.getId());

        kafkaService.emitDeviceDemand(targetimei, ConstKafka.DEVICE_TYPE_DownPPmessage, kafkaData, null);
    }

    /**
     * 登录时间更新
     *
     * @param device
     * @param message
     * @time: 2021/1/23 10:03
     * @author: Rock
     * @version 1.0
     */
    private void type_loginTime(Device device, JSONObject message) {
        Update udpateDevice = new Update();
        if (StringUtil.isNumeric(message.get("firstLoginTime"))) {
            Double timeStamp = Double.parseDouble(message.get("firstLoginTime").toString());
            if (StringUtil.isBlank(device.getFirstLoginTimeStamp())) {
                udpateDevice.set("firstLoginTimeStamp", timeStamp);
            }
            if (StringUtil.isBlank(device.getFirstLoginTime())) {
                udpateDevice.set("firstLoginTime", new Date(timeStamp.longValue() * 1000));
                udpateDevice.set("firstLoginTimeStamp", timeStamp);
            }
        }
        if (StringUtil.isNumeric(message.get("lastLoginTime"))) {
            Double timeStamp = Double.parseDouble(message.get("lastLoginTime").toString());
            udpateDevice.set("lastLoginTime", new Date(timeStamp.longValue() * 1000));
            udpateDevice.set("lastLoginTimeStamp", timeStamp);
        }
        String imsi = message.getString("imsi");
        String modelId = device.getModelId();
        DeviceModels deviceModels = null;
        if (modelId != null && modelId.length() > 0) {
            deviceModels = deviceModelsService.findById(modelId);
        }
        if (StringUtil.isNotBlank(imsi) && imsi.length() > 5) {
            String temp = imsi.substring(3, 5);
            //默认使用移动
            int network_operator = 0;
            if ("01".equals(temp) || "06".equals(temp) || "09".equals(temp) || "20".equals(temp)) {
                //联通
                network_operator = 1;
            } else if ("03".equals(temp) || "05".equals(temp) || "11".equals(temp) || "12".equals(temp)) {
                //电信
                network_operator = 2;
            }
            udpateDevice.set("network_operator", network_operator);
            udpateDevice.set("imsi", imsi);
            Date imsiDate = new Date();
            if (message.containsKey("imsitimestamp") && StringUtil.isNumeric(message.get("imsitimestamp"))) {
                Double timeStamp = Double.parseDouble(message.get("imsitimestamp").toString());
                imsiDate = new Date(timeStamp.longValue() * 1000);
            }
            //检查更新时间
            deviceImsiService.checkUpdate(device.getId(), imsi, imsiDate);
            /*
            //增加上报imsi(ICCID指令)时如果设备型号中有设置定位间隔,就下发一次定位间隔设置指令
            try:
                model_id = device.get("model_id")
                modelMOB = ModelsModel.mustFindOne(model_id)
                positioningTimeInterval = modelMOB.get("positioningTimeInterval", None)
                if modelMOB and positioningTimeInterval:
                    emitDeviceDemand(imei, {'type': 'locationUploadInterval', 'second': int(positioningTimeInterval),
                                            'openid': "autoDown"})
            except Exception as error:
                logger.info("error: %s 堆栈:%s" % (error.message, traceback.format_exc()))
            */
            try {
                if (deviceModels != null) {
                    if (deviceModels.getPositioningTimeInterval() != null && StringUtil.isNumeric(deviceModels.getPositioningTimeInterval())) {
                        //下发 设置设备定位间隔
                        JSONObject kafkaData = new JSONObject();
                        kafkaData.put("imei", device.getId());
                        kafkaData.put("vender", message.getString("vender"));
                        kafkaData.put("second", Integer.valueOf(deviceModels.getPositioningTimeInterval()));
                        kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_LocationUploadInterval, kafkaData, "autoDown");
                    }
                }

            } catch (Exception e) {
                log.error("imei:" + device.getId() + " 自动下发定位配置失败:" + e.getMessage(), e);
            }
        }
        int devOrientGroup = 0;
        if (message.containsKey("autoModules")) {

            if (deviceModels != null) {
                Integer isWatchCustomFunction = deviceModels.getIsWatchCustomFunction();
                if (isWatchCustomFunction != null && isWatchCustomFunction == 1) {
                    udpateDevice.set("watchCustomFunction", message.get("autoModules"));
                    if (message.getString("autoModules").contains("DEVTYPE:")) {
                        String[] tempArr = message.getString("autoModules").split("DEVTYPE:");
                        tempArr = tempArr[tempArr.length - 1].split(",");
                        if (StringUtil.isNumeric(tempArr[0])) {
                            devOrientGroup = Integer.parseInt(tempArr[0]);
                        }
                    }
                }
                udpateDevice.set("devOrientGroup", devOrientGroup);
            }
        }

        if (message.containsKey("platform")) {
            udpateDevice.set("platform", message.get("platform"));
        }
        if (message.containsKey("devModel")) {
            udpateDevice.set("devModel", message.get("devModel"));
        }
        if (message.containsKey("version")) {
            udpateDevice.set("version", message.get("version"));
        }
        //设备的默认语言
        if (message.containsKey("language")) {
            udpateDevice.set("language", message.get("language"));
        }
        deviceService.updateById(device.getId(), udpateDevice);
        if (message.containsKey("battery")) {
            this.type_battery(device, message);
        }
        String dmpAccountId = device.getDmpAccountId();
        if (dmpAccountId != null) {
            // 要上传数据到 联通DMP 平台的主题
            log.info("imei={} 要上传数据到 联通DMP 平台: dmpAccountId: ", device.getId(), dmpAccountId);
            // kafka.producer.topic-name-dmpDevice=jianyou_flp_DMP_Test
            JSONObject tempValue = new JSONObject();
            tempValue.put("dmpAccountId", dmpAccountId);
            tempValue.put("type", "login");
            if (device.getBattery() != null && device.getBattery() != 0) {
                tempValue.put("battery", "" + device.getBattery());
            }

            kafkaService.sendDMPDeviceWithMsg(device.getId(), tempValue);
        }
    }


    /**
     * 手表 上报扫描词
     *
     * @param device
     * @param message
     * @time: 2023/8/30 18:05
     * @author: Weichao
     * @version 1.0
     */
    private void type_upScanWord(Device device, JSONObject message) {
        if (message.getJSONArray("list") != null) {
            JSONArray list = message.getJSONArray("list");
            //当天日期:
            // 创建一个格式化日期时间的 SimpleDateFormat 对象
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
            Date nowDate = new Date();
            String todayDateStr = sdf.format(nowDate);
            if (list.size() > 0) {
                try {
                    //redis中查
                    boolean isAdd = true;
                    //还能添加的差额多少个
                    int tempNum = 0;
                    String todayUpdateCount = redisFilterService.get(deviceSanWordService.deviceSanWordUpdateDayCountKey + device.getId() + "&" + todayDateStr);
                    if (todayUpdateCount == null) {
                        todayUpdateCount = "0";
                        tempNum = list.size();
                    } else {
                        Integer limitNum = Integer.valueOf(deviceSanWordService.deviceSanWordUpdateDauLimit);
                        if (Integer.valueOf(todayUpdateCount) >= limitNum) {
                            isAdd = false;
                        } else {
                            tempNum = limitNum - Integer.valueOf(todayUpdateCount);
                        }
                    }
                    String adminOpenid = null;
                    //找管理员,管理员有授权的情况下:
                    Map<String, Map<String, Object>> userMap = device.getUserMap();
                    if (userMap != null) {
                        //如果判断绑定这个表的用户是有管理员的
                        for (String openid : userMap.keySet()) {
                            JSONObject tempItem = new JSONObject(userMap.get(openid));
                            if ("admin".equals(tempItem.getString("identity"))) {
                                //是管理员
                                adminOpenid = openid;
                                break;
                            }
                        }
                    }
                    JSONArray addList = new JSONArray();
                    for (int i = 0; i < list.size(); i++) {
                        if (i > tempNum) {
                            log.info("imei:{} 上传生词 超过了限额, 剩下余额差额tempNum:{} 上传个数:{} 总message:{}", device.getId(), tempNum, list.size(), message);
                            break;
                        } else {
                            JSONObject item = list.getJSONObject(i);
                            addList.add(deviceSanWordService.add(device.getId(), item));
                        }
                    }
                    if (adminOpenid != null && !addList.isEmpty()) {
                        deviceSanWordService.addListAndUpCloud(device.getId(), addList, adminOpenid);
                    }

                    Integer updateWordCount = device.getUpdateWordCount();

                    int endAddNum = list.size() > tempNum ? tempNum : list.size();

                    updateWordCount += endAddNum;
                    //更新一下单词总上传个数
                    deviceService.updateById(device.getId(), new Update().set("updateWordCount", updateWordCount));

                    endAddNum += Integer.valueOf(todayUpdateCount);

                    long nowTime = DateUtil.nowTimestampToLong();
                    //当前距离今天结束还剩多少秒
                    long todayEndLimitTime = (24 * 60 * 60) - (nowTime % (24 * 60 * 60));
                    redisFilterService.setex(deviceSanWordService.deviceSanWordUpdateDayCountKey + device.getId() + "&" + todayDateStr, (int) todayEndLimitTime, "" + endAddNum);

                } catch (Exception e) {
                    log.error("imei:" + device.getId() + " 上传生词 有问题:" + e.getMessage(), e);
                }

            }

        }
    }

    /**
     * 设备 上报应用记录
     *
     * @param device
     * @param message
     * @time: 2023/12/20 18:05
     * @author: Weichao
     * @version 1.0
     */
    private void type_upAppRecord(Device device, JSONObject message) {
        /*
        "useHistory" : <list> // 使用记录
          [
            {
            "recordID": "16位随机字符串", // <string> 此条记录的ID,防止重复上传问题. 16位随机字符串
            "appName": "XXXX", // <string> 应用名称
            "startTime": 1702522996, // <long> 开始使用时间戳 (秒)
            "endTime": 1702523000 // <long> 结束使用时间戳 (秒)
            },
            ...
          ]
        */
        JSONArray useHistory = message.getJSONArray("useHistory");
        if (!useHistory.isEmpty()) {
            for (int i = 0; i < useHistory.size(); i++) {
                JSONObject item = useHistory.getJSONObject(i);
                deviceAppRecordService.add(device.getId(), item);
            }
            log.info("imei:{} 上传应用记录 useHistory:{} 个", device.getId(), useHistory.size());
        }
    }


    /**
     * 设备发起请求 和彩云移动云盘相册功能 (关闭/授权/更新/通知)
     *
     * @param device
     * @param message
     * @time: 12/21/22 5:38 PM
     * @author: Weichao
     * @version 1.0
     */
    private void type_151(Device device, JSONObject message) {
        JSONObject kafkaMsgOB = new JSONObject();
        Integer q_type = message.getInteger("q_type");
        // 设备的电话号码
        String phone = message.getString("phone");
        if (!devHCYAuthService.checkPhone(phone)) {
            kafkaMsgOB.put("status", 0);
            kafkaMsgOB.put("errMsg", "设备手机号不合规");
            kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_HCY_Q_Status, kafkaMsgOB, null);
            return;
        }
        String imei = device.getId();
        kafkaMsgOB.put("devNickName", deviceService.getDeviceName(device));

        DevHCYAuth devHCYAuth = devHCYAuthService.findById(imei);

        try {
            if (devHCYAuth == null || devHCYAuth.getFamily_auth() == 0) {
                if (devHCYAuth == null) {
                    devHCYAuth = new DevHCYAuth();
                    devHCYAuth.setId(imei);
                    if (device.getWatchCustomFunction() != null) {
                        if (device.getWatchCustomFunction().contains("NIUMAN")) {
                            devHCYAuth.setProject_type(0);
                        } else if (device.getWatchCustomFunction().contains("XIAOXUN")) {
                            devHCYAuth.setProject_type(1);
                        } else if (device.getWatchCustomFunction().contains("YDYP_NRX")) {
                            //诺而信
                            devHCYAuth.setProject_type(2);
                        }
                    } else {
                        log.error("imei:{} 设备自定义配置, 设备指令上传有误", imei);
                        return;
                    }
                    devHCYAuth.setCreatDate(new Date());
                    devHCYAuth.setUpdateDate(new Date());
                    devHCYAuthService.save(devHCYAuth);
                }
                //失败
                kafkaMsgOB.put("status", 0);
                kafkaMsgOB.put("errMsg", "设备未被家长授权");
                kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_HCY_Q_Status, kafkaMsgOB, null);
                return;
            }
            if (devHCYAuth.getFamily_auth_uuid() == null) {
                //失败
                kafkaMsgOB.put("status", 0);
                kafkaMsgOB.put("errMsg", "设备未被家长授权");
                kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_HCY_Q_Status, kafkaMsgOB, null);
                return;
            }

            //token 和 token的失效期
            String dev_accesstoken = devHCYAuth.getDev_accesstoken();
            Long dev_accesstoken_expiry = devHCYAuth.getDev_accesstoken_expiry();

            /*
            q_type:

            -1: 关闭(退出)相册

            0: 请求授权

            1: 请求更新accesstoken

            2: 设备新增的相册图片等,通知服务器(APP上理应会有小红点提示)
            */
            if (q_type == -1) {
                //关闭(退出)相册
                devHCYAuth.setIsOff(1);
                devHCYAuth.setUpdateDate(new Date());
                return;
            }
            if (q_type == 0 || q_type == 1) {
                //相册是否是关闭状态
                if (devHCYAuth.getIsOff() == 1) {
                    devHCYAuth.setIsOff(0); //变成打开
                }
                //如果在 有效期内刷新Token
                JSONObject resultOB = devHCYAuthService.devHCYRefreshToken(device, devHCYAuth);
                if (resultOB != null) {
                    kafkaMsgOB.put("status", 2);
                    kafkaMsgOB.put("accesstoken", resultOB.getString("accesstoken"));
                    kafkaMsgOB.put("expiresTime", resultOB.getLong("expiresTime"));
                    kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_HCY_Q_Status, kafkaMsgOB, null);
                    return;
                }
            }
            if (q_type == 2) {
                //保存一下,当前家庭成员的列表未读
                devHCYAuth.setHcyPhotosUnreadListStr(JSON.toJSONString(device.getUserMap().keySet()));
                return;
            }


            //====== 替手表请求授权 ======
            //拼接中间页
            String redirectUrl = "http://open.yun.139.com/#/home"; //手表请求的这里无需填正确的回调地址
            // 请求ID :base64（appid_yyyyMMddHHmmssSSS+3位随机数）生成。登录成功后用于换取开放平台接口调用所需的唯一凭证accesstoken。
            String qId = String.format("%s_%s%s", devHCYAuthService.getAppID(devHCYAuth), DateUtil.dateToStr(new Date(), "yyyyMMddHHmmssSSS"), RandomUtil.getRandom(100, 999));
            String qIdBase64Str = new String(Base64.getEncoder().encode(qId.getBytes()));
            // 设备的电话号码
            devHCYAuth.setPhone(phone);
            String urlParam = String.format("?pageType=3&appId=%s&appKey=%s&appTitle=%s&deviceId=%s&uuid=%s&mobile=%s&channelId=%s&redirectUrl=%s",
                    devHCYAuthService.getAppID(devHCYAuth),
                    devHCYAuthService.getAppKey(devHCYAuth),
                    devHCYAuthService.getAppTitle(devHCYAuth),
                    device.getId(),
                    URLEncoder.encode(qIdBase64Str, "UTF-8"), //此参数用户换取token //单独 URL编码一下
                    phone,
                    devHCYAuthService.HCYDevChannelId,
                    URLEncoder.encode(redirectUrl, "UTF-8")); //单独 URL编码一下
            String middlePageUrl = devHCYAuthService.HCYMiddlePageURL + urlParam;
            //请求
            String hcyMiddlePageResult = HttpReq.sendGet(middlePageUrl);

            // 和彩云相册中间页获取验证码
            JSONObject getVCodeParam = new JSONObject();
            /*
            {
                "appId": "159857278877866XXXX",
                "msisdn": "hMZIjqRka1wE0ldvBahdlg==",
                "channelId": "10003" //代替设备时用 10003,
            }
            */
            getVCodeParam.put("appId", devHCYAuthService.getAppID(devHCYAuth));
            //phone 需要加密
            String phoneEncryptData = AES256ECB.encryptData(phone, devHCYAuthService.getAppKey(devHCYAuth));
            getVCodeParam.put("msisdn", phoneEncryptData);

            getVCodeParam.put("channelId", devHCYAuthService.HCYDevChannelId);
            String getVcodeRes = HttpReq.sendPostJson(devHCYAuthService.HCYGetVcodeURL, getVCodeParam.toJSONString());
            JSONObject getVCodeResOB = JSONObject.parseObject(getVcodeRes);
            if (getVCodeResOB.getInteger("code") == 0) {
                log.info("imei:{} 和彩云相册 获取验证码成功 getVcodeRes:{}", imei, getVcodeRes);
                /*
                {
                    "code": 0,
                    "totalRows": 0,
                    "message": "成功",
                    "data": {
                        "random": "0c1ced08-61aa-4a99-8358-386621bef3b5"
                    }
                }
                */
                //设置 随机请求数 到Redis 中去. 5分钟有效期
                String random = getVCodeResOB.getJSONObject("data").getString("random");
                String resStatus = RedisClusterService.getJedisCluster().setex(qIdBase64Str, 5 * 60, random);
//                log.info("imei:{} 服务器代设备 手机 请求验证码成功, 设置到redis中缓存:{} qId:{} random:{}", imei, resStatus, qId, random);
                //3d9502b5-318b-49a6-b6cd-c25f2fe07385 (random)
                //MTU5ODU3Mjc4ODc3ODY2ODAzMl8yMDIyMTIxNzE0NDAyNzAwMDEyMw== (qId)
                kafkaMsgOB.put("status", 1);
                kafkaMsgOB.put("qId", qIdBase64Str);
                kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_HCY_Q_Status, kafkaMsgOB, null);
            } else {
                log.error("imei:{} 服务器代设备 手机 :{} 请求验证码错误:{}", imei, phone, getVcodeRes);
                //失败
                kafkaMsgOB.put("status", 0);
                kafkaMsgOB.put("errMsg", "服务器代设备手机请求验证码错误");
                if (getVCodeResOB.containsKey("message")) {
                    kafkaMsgOB.put("errMsg", getVCodeResOB.getString("message"));
                }
                kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_HCY_Q_Status, kafkaMsgOB, null);
                return;
            }


        } catch (Exception e) {
            log.error("imei:" + imei + "  设备请求授权和彩云移动云盘相册功能 错误:" + e.getMessage(), e);

        } finally {
            //添加日志记录
            HCYAuthLog hcyAuthLog = new HCYAuthLog();
            hcyAuthLog.setId(UUIDGenerator.generate());
            hcyAuthLog.setImei(imei);
            hcyAuthLog.setDevPhone(phone);
            //授权类型:
            // 0:家长授权 1:设备自己登录授权 2:设备邀请加入云相册
            // 3:家长自己登录授权 4:家长加入云相册  5:设备关闭云相册
            if (q_type == -1) {
                hcyAuthLog.setType(5);
            } else if (q_type == 0 || q_type == 1) {
                hcyAuthLog.setType(1);
            } else {
                //其他的就不存日志了, 防止浪费
                devHCYAuthService.save(devHCYAuth);
                return;
            }

            hcyAuthLog.setCreatDate(new Date());
            hcyAuthLogService.save(hcyAuthLog);
            devHCYAuthService.save(devHCYAuth);

        }
    }

    /**
     * 设备上传收到云相册请求授权的验证码
     *
     * @param device
     * @param message
     * @time: 12/21/22 5:38 PM
     * @author: Weichao
     * @version 1.0
     */
    private void type_153(Device device, JSONObject message) {
        JSONObject kafkaMsgOB = new JSONObject();
        Integer q_type = message.getInteger("q_type");
        // 设备的电话号码
        String phone = message.getString("phone");
        String imei = device.getId();
        kafkaMsgOB.put("devNickName", deviceService.getDeviceName(device));

        //中间授权页的 uuid请求串 也能通过redis或得 验证码的随机串
        String qId = message.getString("qId");
        String vCodeRandom = RedisClusterService.getJedisCluster().get(qId);
        String vCode = message.getString("vCode");
        if (vCodeRandom != null) {
            try {
                DevHCYAuth devHCYAuth = devHCYAuthService.findById(imei);
                // 和彩云相册中间页提交验证码 ===> 成功后用 qId (中间授权页的 uuid请求串) 得到 token
                JSONObject postVCodeParam = new JSONObject();
                /*
                {
                    "appId": "1598572788778668032",
                    "msisdn": "hMZIjqRka1wE0ldvBahdlg==",
                    "random": "3d9502b5-318b-49a6-b6cd-c25f2fe07385",
                    "verifycode": "583133",
                    "deviceId": "helloworld",
                    "uuid": "MTU5ODU3Mjc4ODc3ODY2ODAzMl8yMDIyMTIxNzE0NDAyNzAwMDEyMw==",
                    "channelId": "10002"
                }
                */

                //phone 需要加密
                String phoneEncryptData = AES256ECB.encryptData(phone, devHCYAuthService.getAppKey(devHCYAuth));
                postVCodeParam.put("appId", devHCYAuthService.getAppID(devHCYAuth));
                postVCodeParam.put("msisdn", phoneEncryptData);
                postVCodeParam.put("random", vCodeRandom);
                postVCodeParam.put("verifycode", vCode);
                postVCodeParam.put("deviceId", imei);
                postVCodeParam.put("uuid", qId);
                postVCodeParam.put("channelId", devHCYAuthService.HCYDevChannelId);
                String postVcodeRes = HttpReq.sendPostJson(devHCYAuthService.HCYPostVcodeURL, postVCodeParam.toJSONString());
                JSONObject postVcodeResOB = JSONObject.parseObject(postVcodeRes);
                if (postVcodeResOB.getInteger("code") == 0) {
                    log.info("imei:{} 设备上传收到云相册请求授权的验证码 成功 postVCodeParam:{} postVcodeRes:{}", imei, postVCodeParam, postVcodeRes);
                    //提交验证码成功 后这时候 用去请求才能获得token
                    JSONObject getTokenParam = new JSONObject();
                    getTokenParam.put("uuid", qId);
                    String getTokenRes = HttpReq.sendPostJson(devHCYAuthService.HCYMiddlePageGetTokenURL, getTokenParam.toJSONString());
                    JSONObject getTokenResOB = JSONObject.parseObject(getTokenRes);
                    if (getTokenResOB.getInteger("code") == 0) {
                        log.info("imei:{}  云相册请求授权的验证码拿到后请求 accesstoken 成功 getTokenParam:{} getTokenRes: {}", imei, getTokenParam, getTokenRes);
                        /*
                        {
                            "code": 0,
                            "message": "成功",
                            "data": {
                                "accessToken": "123",
                                "expiresIn": 168
                            }
                        }
                        */
                        JSONObject data = getTokenResOB.getJSONObject("data");
                        String newToken = data.getString("accessToken");
                        Long nowTime = DateUtil.nowTimestampToLong();
                        Long newTokenExpiresIn = nowTime + data.getInteger("expiresIn") * 3600;
                        devHCYAuth.setDev_accesstoken(newToken);
                        devHCYAuth.setDev_accesstoken_expiry(newTokenExpiresIn);
                        devHCYAuth.setDev_accesstoken_update(new Date());
                        devHCYAuth.setUpdateDate(new Date());
                        devHCYAuthService.save(devHCYAuth);

                        kafkaMsgOB.put("status", 2);
                        kafkaMsgOB.put("accesstoken", newToken);
                        kafkaMsgOB.put("expiresTime", newTokenExpiresIn);
                        kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_HCY_Q_Status, kafkaMsgOB, null);
                        return;
                    }
                } else {
                    log.error("imei:" + imei + " 设备上传收到云相册请求授权的验证码(153) 提交验证码失败:" + postVcodeRes);
                    //失败
                    kafkaMsgOB.put("status", 0);
                    kafkaMsgOB.put("errMsg", "服务器代设备手机提交验证码失败");
                    kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_HCY_Q_Status, kafkaMsgOB, null);
                    return;
                }
            } catch (Exception e) {
                log.error("imei:" + imei + " 设备上传收到云相册请求授权的验证码(153) 错误:" + e.getMessage(), e);

            }

        } else {
            log.error("imei:{} 设备提交的请求授权的单号 qId 有误 (153)", imei);
            //失败
            kafkaMsgOB.put("status", 0);
            kafkaMsgOB.put("errMsg", "设备提交的请求授权的单号 qId 有误");
            kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_HCY_Q_Status, kafkaMsgOB, null);
            return;
        }


    }

    /**
     * 设备端邀请家长加入云盘相册
     *
     * @param device
     * @param message
     * @time: 12/21/22 5:38 PM
     * @author: Weichao
     * @version 1.0
     */
    private void type_155(Device device, JSONObject message) {
        // 开通成功的家庭云ID
        String cloudID = message.getString("cloudID");
        // 开通成功的云相册ID
        String photoID = message.getString("photoID");
        String imei = device.getId();
        DevHCYAuth devHCYAuth = devHCYAuthService.findById(imei);
        devHCYAuth.setCloudID(cloudID);
        devHCYAuth.setAlbumld(photoID);
        devHCYAuthService.save(devHCYAuth);
        if (devHCYAuth.getPhone() != null && devHCYAuth.getPhone().length() != 0) {
            HCYPhoneCloud hcyPhoneCloud = hcyPhoneCloudService.findById(devHCYAuth.getPhone());
            if (hcyPhoneCloud == null) {
                hcyPhoneCloud = new HCYPhoneCloud();
                hcyPhoneCloud.setId(devHCYAuth.getPhone());
                hcyPhoneCloud.setCloudID(cloudID);
                hcyPhoneCloud.setAlbumld(photoID);
                hcyPhoneCloud.setProject_type(devHCYAuth.getProject_type());
                hcyPhoneCloud.setCreatDate(new Date());

            } else {
                hcyPhoneCloud.setCloudID(cloudID);
                hcyPhoneCloud.setAlbumld(photoID);
            }
            hcyPhoneCloud.setUpdateDate(new Date());
            hcyPhoneCloudService.save(hcyPhoneCloud);
            log.info("imei:{} phone:{} 和彩云后台存储 手机号<=>家庭云ID的记录 cloudID:{} Albumld:{}", imei, devHCYAuth.getPhone(), cloudID, photoID);
        }
        //发送一条群组类型消息

        String content = String.format("%s 已开通云相册服务功能，点此加入云相册，共享孩子成长点滴。", deviceService.getDeviceName(device));
        for (String key : device.getChatGroupMap().keySet()) {
            ObjectId gid = new ObjectId(key);
            //表拥有的群组 都插入一条聊天消息
            ChatGroupMessage chatGroupMessage = new ChatGroupMessage();
            chatGroupMessage.setGroupid(gid);
            chatGroupMessage.setSendType(2);
            chatGroupMessage.setSender(device.getId());
            chatGroupMessage.setMessageType(100);//邀请用户加入云相册
            chatGroupMessage.setContent(content);
            chatGroupMessageService.add(chatGroupMessage);
            //加上聊天群的角标
            ChatGroup chatGroup = chatGroupService.findById(gid);
            if (CollectionUtil.isNotEmpty(chatGroup.getMemberMap())) {
                FindIterable<Document> accountDocs = accountService.listDocByIds(chatGroup.getMemberMap().keySet());
                for (Document doc : accountDocs) {
                    Update updateAccount = new Update();
                    updateAccount.set(key, doc.getInteger(key) == null ? 1 : doc.getInteger(key) + 1);
                    updateAccount.set("badge", doc.getInteger("badge") == null ? 1 : doc.getInteger("badge") + 1);
                    accountService.updateById(doc.getString("_id"), updateAccount);
                }
            }

            //发送kafka
            JSONObject subJson = new JSONObject();
            subJson.put("id", chatGroupMessage.getId());
            subJson.put("senderID", device.getId());
            subJson.put("senderType", 2);
            subJson.put("type", 100);
            subJson.put("content", content);
            subJson.put("msgTitle", content);
            subJson.put("timestamp", DateUtil.nowTimestampToDouble());
            JSONObject pushData = new JSONObject();
            pushData.put("type", ConstKafka.CHAT_GROUP_MESSAGE);
            pushData.put("groupid", key);
            pushData.put("message", subJson);
            /*
            {
                "groupid":"6334f3dbdcXXX",
                "type":"chatGroupMessage",
                "message":{
                    "senderID":"862650050604XXX",
                    "msgTitle":"XXX 已开通云相册服务功能，点此加入云相册，共享孩子成长点滴。",
                    "senderType":2,
                    "id":"63b542b12f2cc322a3a43XXX",
                    "type":100,
                    "content":"XXX 已开通云相册服务功能，点此加入云相册，共享孩子成长点滴。",
                    "timestamp":1672823473.513
                }
            }
            */
            kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_1, key, pushData);
        }

    }

    /**
     * 设备请求家长授权(授权孩子使用云相册)
     *
     * @param device
     * @param message
     * @time: 12/21/22 5:38 PM
     * @author: Weichao
     * @version 1.0
     */
    public void type_157(Device device, JSONObject message, boolean isPushAPP) {
        //发送所有绑定此表的 用户推送通知 通知其调用接口授权使用
        //手表请求授权云相册 手表具备云相册服务功能，支持备份手表照片视频到中国移动云盘存储空间，防止数据丢失，请你阅读并同意隐私政策和用户协议，确认授权您孩子使用云相册服务功能。
        String content = String.format("%s 请求授权云相册服务功能，支持备份手表照片视频到中国移动云盘存储空间，防止数据丢失，请你阅读并同意隐私政策和用户协议，确认授权您孩子使用云相册服务功能。", deviceService.getDeviceName(device));
        for (String key : device.getChatGroupMap().keySet()) {
            ObjectId gid = new ObjectId(key);
            //表拥有的群组 都插入一条聊天消息
            ChatGroupMessage chatGroupMessage = new ChatGroupMessage();
            chatGroupMessage.setGroupid(gid);
            chatGroupMessage.setSendType(2);
            chatGroupMessage.setSender(device.getId());
            chatGroupMessage.setMessageType(101);//邀请用户加入云相册
            chatGroupMessage.setContent(content);
            if (isPushAPP == false) {
                //往后偏移1s
                chatGroupMessageService.add(chatGroupMessage, 1);
            } else {
                chatGroupMessageService.add(chatGroupMessage);
            }

            //加上聊天群的角标
            ChatGroup chatGroup = chatGroupService.findById(gid);
            if (CollectionUtil.isNotEmpty(chatGroup.getMemberMap())) {
                FindIterable<Document> accountDocs = accountService.listDocByIds(chatGroup.getMemberMap().keySet());
                for (Document doc : accountDocs) {
                    Update updateAccount = new Update();
                    updateAccount.set(key, doc.getInteger(key) == null ? 1 : doc.getInteger(key) + 1);
                    updateAccount.set("badge", doc.getInteger("badge") == null ? 1 : doc.getInteger("badge") + 1);
                    accountService.updateById(doc.getString("_id"), updateAccount);
                }
            }

            //发送kafka
            JSONObject subJson = new JSONObject();
            subJson.put("id", chatGroupMessage.getId());
            subJson.put("senderID", device.getId());
            subJson.put("senderType", 2);
            subJson.put("type", 101);
            subJson.put("content", content);
            subJson.put("msgTitle", content);
            subJson.put("timestamp", DateUtil.nowTimestampToDouble());
            if (isPushAPP) {
                JSONObject pushData = new JSONObject();
                pushData.put("type", ConstKafka.CHAT_GROUP_MESSAGE);
                pushData.put("groupid", key);
                pushData.put("message", subJson);
                /*
                {
                    "groupid":"6334f3dbdcXXX",
                    "type":"chatGroupMessage",
                    "message":{
                        "senderID":"862650050604XXX",
                        "msgTitle":"XXX 请求授权云相册服务功能，支持备份手表照片视频到中国移动云盘存储空间，防止数据丢失，请你阅读并同意隐私政策和用户协议，确认授权您孩子使用云相册服务功能。",
                        "senderType":2,
                        "id":"63b542b12f2cc322a3a43XXX",
                        "type":101,
                        "content":"XXX 请求授权云相册服务功能，支持备份手表照片视频到中国移动云盘存储空间，防止数据丢失，请你阅读并同意隐私政策和用户协议，确认授权您孩子使用云相册服务功能。",
                        "timestamp":1672823473.513
                    }
                }
                */
                kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_1, key, pushData);
            }
        }
    }

    /**
     * @param [device, message]
     * @return void
     * @description APP应用监管-应用上报
     * @author cwj
     * @date @time 2024/11/14 12:02
     **/
    private void type_appFn(Device device, JSONObject message) {
        //查询App应用信息
        DeviceAppManager deviceAppManager = deviceAppManagerService.queryDeviceAppManagerByImei(device.getId());
        JSONArray appDatas = message.getJSONArray("appDatas");
        Integer totalCount = message.getInteger("appCount");
        //保存或更新数据
        if (ObjectUtils.isEmpty(deviceAppManager)) {
            //保存App应用
            saveDeviceAppManager(device, totalCount, appDatas);
        } else {
            //更新App应用
            updateDeviceAppManager(appDatas, deviceAppManager, totalCount);
        }
    }

    /**
     * @param [device, message]
     * @return void
     * @description 保存喝水上报的记录
     * @author cwj
     * @date @time 2024/12/3 12:02
     **/
    private void type_drinkData(Device device, JSONObject message) {
        String nowDate = DateUtil.dateToStr(new Date(), "yyyy-MM-dd");
        DrinkData drinkData = new DrinkData();
        drinkData.setImei(device.getId());
        drinkData.setDrinkLitre(message.getIntValue("value"));
        drinkData.setType(message.getIntValue("drinkType"));
        drinkData.setReportTime(message.getLong("drinkTime"));
        drinkData.setDate(nowDate);
        drinkData.setCreateTime(new Date());
        drinkData.setUpdateTime(new Date());
        drinkDataService.save(drinkData);
    }

    /**
     * @param [device, message]
     * @return void
     * @description 解析通话记录数据
     * @author cwj
     * @date @time 2025/3/4 15:33
     **/
    private void type_callLog(Device device, JSONObject message) {
        CallRecord callRecord = new CallRecord();
        callRecord.setImei(device.getId());
        callRecord.setPhoneNum(message.getString("phoneNum"));
        callRecord.setContactName(message.getString("contactName"));
        callRecord.setCallType(message.getInteger("callType"));
        callRecord.setCallStatus(message.getInteger("callStatus"));
        callRecord.setCallStartTime(DateUtil.timestampToStr(message.getLong("callStartTime") * 1000, DateUtil.FORMAT_1));
        callRecord.setCallStartTimestamp(message.getLong("callStartTime"));
        callRecord.setDurationSecond(message.getInteger("durationSecond"));
        callRecord.setCreateTime(new Date());
        callRecord.setUpdateTime(new Date());
        callRecordService.save(callRecord);
        //推送消息
        pushAccountMessage2(device, "callLog", "API.pushTitle.call.log", null, null, false, true);
    }


    /**
     * @param [appDatas, deviceAppManager, totalCount]
     * @return void
     * @description 更新App应用
     * @author cwj
     * @date @time 2024/11/14 17:16
     **/
    private void updateDeviceAppManager(JSONArray appDatas, DeviceAppManager deviceAppManager, Integer totalCount) {
        boolean isChange = false;
        //更新数据
        if (!ObjectUtils.isEmpty(appDatas)) {
            Update update = new Update();
            update.set("updateDate", new Date());
            if (!ObjectUtils.isEmpty(totalCount) && !deviceAppManager.getTotalCount().equals(totalCount)) {
                isChange = true;
                update.set("totalCount", totalCount);
            }
            List<AppUidName> appUidNames = deviceAppManager.getAppUidNames();
            List<AppUidName> appUidNameList = new ArrayList<>();
            if (!ObjectUtils.isEmpty(appDatas)) {
                if (CollectionUtils.isEmpty(appUidNames)) {
                    isChange = true;
                    //组装AppUidName的集合数据
                    assembleAppUidName(appDatas, appUidNames);
                    update.set("appUidNames", appUidNames);
                } else {
                    //比对差异性
                    for (int i = 0; i < appDatas.size(); i++) {
                        String appData = appDatas.getString(i);
                        if (StringUtils.isNotEmpty(appData)) {
                            boolean isAppChange = false;
                            String[] appDataStrs = appData.split("-");
                            if (!ObjectUtils.isEmpty(appDataStrs) && appDataStrs.length >= 2) {
                                String appUid = appDataStrs[0];
                                String appName = appDataStrs[1];
                                int count = 0;
                                //比对是否有变化
                                for (int j = 0; j < appUidNames.size(); j++) {
                                    AppUidName aun = appUidNames.get(j);
                                    //存在相同应用
                                    if (aun.getAppName().equals(appName) || aun.getAppUid().equals(appUid)) {
                                        //更新有变化的部分
                                        if (!aun.getAppUid().equals(appUid)) {
                                            isAppChange = true;
                                            aun.setAppUid(appUid);
                                        }
                                        if (!aun.getAppName().equals(appName)) {
                                            isAppChange = true;
                                            aun.setAppName(appName);
                                        }
                                        appUidNameList.add(aun);
                                        break;
                                    } else {
                                        count++;
                                    }
                                }
                                //应用之前就不存在，则直接新增
                                if (!isAppChange && count == appUidNames.size()) {
                                    isChange = true;
                                    AppUidName appUidName = new AppUidName();
                                    appUidName.setAppUid(appUid);
                                    appUidName.setAppName(appName);
                                    appUidName.setIsOpen("1");
                                    appUidNameList.add(appUidName);
                                }
                                if (isAppChange) {
                                    isChange = true;
                                }
                            }
                        }
                    }
                    if (!CollectionUtils.isEmpty(appUidNameList)) {
                        update.set("appUidNames", appUidNameList);
                    }
                }
            }
            //有变化更新记录
            if (isChange) {
                deviceAppManagerService.upadteDeviceAppManagerById(deviceAppManager.getId(), update);
            }
        }
    }

    /**
     * @param [device, message, appDatas]
     * @return void
     * @description 保存App应用管理
     * @author cwj
     * @date @time 2024/11/14 16:04
     **/
    private void saveDeviceAppManager(Device device, Integer totalCount, JSONArray appDatas) {
        DeviceAppManager deviceAppManager;
        if (!ObjectUtils.isEmpty(appDatas)) {
            //保存数据
            deviceAppManager = new DeviceAppManager();
            Date date = new Date();
            deviceAppManager.setImei(device.getId());
            deviceAppManager.setCreateDate(date);
            deviceAppManager.setUpdateDate(date);
            deviceAppManager.setTotalCount(totalCount);
            List<AppUidName> appUidNames = new ArrayList<>();
            //组装AppUidName的集合数据
            assembleAppUidName(appDatas, appUidNames);
            if (!CollectionUtils.isEmpty(appUidNames)) {
                log.info("------------保存App应用指令---------");
                deviceAppManager.setAppUidNames(appUidNames);
                deviceAppManagerService.save(deviceAppManager);
            }
        }
    }

    /**
     * @param [appDatas, appUidNames]
     * @return void
     * @description 组装AppUidName的数据
     * @author cwj
     * @date @time 2024/11/14 16:42
     **/
    private void assembleAppUidName(JSONArray appDatas, List<AppUidName> appUidNames) {
        for (int i = 0; i < appDatas.size(); i++) {
            String appData = appDatas.getString(i);
            if (StringUtils.isNotEmpty(appData)) {
                String[] appDataStrs = appData.split("-");
                if (!ObjectUtils.isEmpty(appDataStrs) && appDataStrs.length >= 2) {
                    AppUidName appUidName = new AppUidName();
                    appUidName.setAppUid(appDataStrs[0]);
                    appUidName.setAppName(appDataStrs[1]);
                    appUidName.setIsOpen("1");
                    appUidNames.add(appUidName);
                }
            }
        }
    }


    /**
     * 解析基站WIFI位置信息
     *
     * @param imei
     * @param message
     * @param resultJson
     * @time: 2021/2/23 17:13
     * @author: Rock
     * @version 1.0
     */
    private void executionLocateFuntion(String imei, JSONObject message, Device device, JSONObject resultJson) {
        log.info("进入WIFI/基站的解析方法中进行定位......");
        JSONArray cells = message.getJSONArray("baseStation");
        Integer cdma = message.getInteger("baseStationType");
        JSONArray wifis = message.getJSONArray("wifi");
        //判断是否符合定位规则的数据
        String locationType = "LBS";
        boolean parsing = false;
        if ((cells != null && cells.size() > 0 && cdma == 0) || (wifis != null && wifis.size() >= 2)) {
            parsing = true;
            if (cells != null && cells.size() > 0 && cdma == 0) {
                if (cells.size() == 1) {
                    JSONObject item = cells.getJSONObject(0);
                    if (item.getInteger("mcc") == 0 &&
                            item.getInteger("mnc") == 0 &&
                            item.getInteger("ci") == 0) {
                        parsing = false; //不合规
                    }
                }
            }
        } else if (cdma == 1 && cells != null && cells.size() > 0) {
            JSONObject cell = cells.getJSONObject(0);
            if (cell.get("sid") != null && !"0".equals(cell.get("sid").toString())
                    && cell.get("nid") != null && !"0".equals(cell.get("nid").toString())
                    && cell.get("bid") != null && !"0".equals(cell.get("bid").toString())) {
                parsing = true;
            }
        }
        if (!parsing) {
            //不合规, 不解析
            JSONObject subJson = new JSONObject();
            subJson.put("info", "OK");
            subJson.put("status", "1");
            subJson.put("infocode", INFO_CODE);

            resultJson.put("locationType", locationType);
            resultJson.put("useBgAddressTips", 0);

            //失败没有data
//            JSONObject result = new JSONObject();
//            result.put("type", "0");
//            subJson.put("result", result);
//            resultJson.put("data", subJson);
            log.warn("imei:{} 不符合定位规则数据： message={}", device.getId(), message);
            return;
        }
        //检查基站相似度、WIFI的相似度
        int code = checkLBSDataWithImei(cells, cdma, wifis, imei);
        if (code == 1) { //相似, 不解析返回上次的值
            String lastReturnLBSData = redisFilterService.get(imei + CacheEnum.LAST_LBS_DATA.getKey());
            if (StringUtil.isNotBlank(lastReturnLBSData)) {
                JSONObject lastData = JSON.parseObject(lastReturnLBSData);
                if (wifis != null && wifis.size() > 0) {
                    locationType = "WIFI";
                }
                resultJson.put("locationType", locationType);
                if (StringUtil.isNumeric(lastData.get("level")) && new Double(Double.parseDouble(lastData.get("level").toString())).intValue() >= 14) {
                    //只有室内定位才使用后台提供的位置
                    resultJson.put("useBgAddressTips", 1);
                } else {
                    resultJson.put("useBgAddressTips", 0);
                }
                if (StringUtils.isEmpty(lastData.getString("infocode")) || !INFO_CODE.equals(lastData.getString("infocode"))) {
                    lastData.put("infocode", INFO_CODE);
                    lastData.put("info", "OK");
                    lastData.put("status", "1");
                }
                resultJson.put("data", lastData);
                //log.info("-------上一次定位结果为-------：{}",resultJson);
                return;
            }
        }

        //判断是否是国外的
        String language = device.getLanguage();
        Integer mcc = message.getInteger("mcc");
        if (mcc == null || mcc == 460) {
            //国内
            //随机 抽取 1/10 的概率 使用维智定位
            int flag = RandomUtil.getRandomInt(0, 100);
            boolean useGaoDe = true;
            // 通过 useWeiZhiLocateBL 控制比例
            log.info("falg={} 通过 useWeiZhiLocateBL 控制使用维智定位的比例: useWeiZhiLocateBL:{} useWeiZhiLocateBL*100 :{}", flag, useWeiZhiLocateBL, useWeiZhiLocateBL * 100);
            if (flag <= useWeiZhiLocateBL * 100) {
                useGaoDe = false;
                //调用维智定位
                toWZ_LBSparsing(message, device, resultJson);
                if (!resultJson.containsKey("data")) {
                    //可判断resultJson 如果没有data 可能是失败, 再调用一次维智定位
//                    toWZ_LBSparsing(message, device, resultJson);
                    //可判断resultJson 如果没有data 使用高德定位重新获取位置
                    useGaoDe = true;
                }
            }
            if (useGaoDe) {
                //调用高德定位
                if (gdBdConfig.getIsBdOrGd() == 1) {
                    log.info("高德定位......");
                    toGD_LBSparsing(message, device, resultJson);
                } else {
                    log.info("百度定位......");
                    toBD_LBSparsing(message, device, resultJson);
                }
            }
        } else {
            //国外
            //调用google 地图进行解析
            toGoogle_LBSparsing(message, device, resultJson);
        }
        //尽量让 APP 使用本地地图SDK解析位置
        resultJson.put("useBgAddressTips", 0);

    }

    /**
     * 把定位解析的结果 缓存到redis
     *
     * @param imei
     * @param data
     * @param isOverseas
     * @time: 2022/2/14 18:07
     * @author: WeiChao
     * @version 1.0
     */
    private void setReturnLBSDataWithImei(String imei, JSONObject data, boolean isOverseas) {
        redisFilterService.setex(imei + CacheEnum.LAST_LBS_DATA.getKey(), CacheEnum.LAST_LBS_DATA.getExpire(), data.toJSONString());
        JSONObject result = data.getJSONObject("result");
        if (result != null) {
            String adcode = result.getString("adcode");
            if (StringUtil.isNotBlank(adcode)) {
                String lastADCode = RedisClusterService.getJedisCluster().hget(imei, ConstCacheField.WEATHER_ADCODE);
                if (StringUtil.isBlank(lastADCode)) {
                    RedisClusterService.getJedisCluster().hset(imei, ConstCacheField.WEATHER_ADCODE, result.getString("adcode"));
                } else {
                    if (!adcode.equals(lastADCode)) {
                        RedisClusterService.getJedisCluster().hset(imei, ConstCacheField.WEATHER_ADCODE, result.getString("adcode"));
                        RedisClusterService.getJedisCluster().hdel(imei, ConstCacheField.WEATHER_TIME);
                    }
                }
            }
            if (StringUtil.isNotBlank(result.get("desc"))) {
                RedisClusterService.getJedisCluster().hset(imei, ConstCacheField.WEATHER_DESC, result.getString("desc"));
            }
            // 当"海外设备"的位置发生更改时 (在设备的定位信息缓存保存的方法里, 应该移除 天气redis中的 "hf_locid:imei" 标识的缓存, 使缓存流程重置)
            if (isOverseas) {
                redisPPMFAndWeatherService.del(ConstCacheField.HF_WEATHER_LOCATIONID_IMEI_PREFIX + imei);
            }
        }
    }

    /**
     * 定位缓存过滤比较
     * 比较 2 个新旧基站信息 是否相似 0: 不相似 1 :相似
     *
     * @param cells
     * @param cdma
     * @param wifis
     * @param imei
     * @time: 2021/1/21 17:24
     * @author: Rock
     * @version 1.0
     */
    private int checkLBSDataWithImei(JSONArray cells, int cdma, JSONArray wifis, String imei) {
        int flag = 0;
        try {
            // 以imei 号为Key的 是基站数据
            // 以imei + 标识 &LastLBSData 为Key的是定位解析结果
            String lastLBSDataStr = redisFilterService.get(imei);
            if (StringUtil.isNotBlank(lastLBSDataStr)) {
                //对比上一次的定位数据
                JSONObject lastLBSData = JSON.parseObject(lastLBSDataStr);
                //基站的相似度
                if (cells != null && lastLBSData.get("baseStation") instanceof JSONArray) {
                    if (TCPUtil.cellsCompare(lastLBSData.getJSONArray("baseStation"), cells) != 1) {
                        return 0;
                    }
                } else {
                    return 0;
                }
                //WIFI的相似情况
                if (wifis != null && lastLBSData.get("wifis") instanceof JSONArray) {
                    if (TCPUtil.wifwsCompare(lastLBSData.getJSONArray("wifis"), wifis) != 1) {
                        //不相似
                        return 0;
                    }
                } else {
                    return 0;
                }
                flag = 1;
                return flag;
            } else {
                return 0;
            }
        } finally {
            //最后出错或者是先要返回 return 的话,需要更新Redis 中的数据
            if (flag != 1) {
                //log.info("imei={} 不相似, 需要更新redis记录的定位数据 flag={}", imei, flag);
                JSONObject data = new JSONObject();
                if (cells != null && cells.size() > 0) {
                    data.put("baseStation", cells);
                }
                if (wifis != null && wifis.size() > 0) {
                    data.put("wifis", wifis);
                }
                redisFilterService.set(imei, data.toJSONString());
            }
        }
    }

    /**
     * 高德地图 逆地理编码
     *
     * @param lon
     * @param lat
     * @time: 2021/1/21 10:12
     * @author: Rock
     * @version 1.0
     */
    private JSONObject revertLonLat(Double lon, Double lat) {
        Map<String, Object> param = new HashMap<>();
        param.put("key", listDataProperties.getAmapWebKey().get(RandomUtil.getRandomInt(0, listDataProperties.getAmapWebKey().size())));
        param.put("location", lon + "," + lat);
        String convertURL = "http://restapi.amap.com/v3/geocode/regeo?" + HttpReq.paramJoint(param);
        String response = HttpReq.sendGet(convertURL);
        log.info("逆地理位置编码 url = {}. response = {}", convertURL, response);
        if (StringUtil.isNotBlank(response)) {
            return JSON.parseObject(response);
        }
        return null;
    }

    /**
     * GPS定位解析方法
     *
     * @param keyProperties
     * @param gpsJson
     * @param imei
     * @param mcc
     * @time: 2/14/22 6:35 PM
     * @author: Weichao
     * @version 1.0
     */
    public static JSONObject toGPSparsing(ListDataProperties keyProperties, JSONObject gpsJson, String imei, Integer mcc, Integer isGdOrBdLBS) {
        if (gpsJson == null) {
            return null;
        }
        if (gpsJson.size() == 0) {
            return null;
        }
        Double lon = StringUtil.isNumeric(gpsJson.get("lon")) ? Double.parseDouble(gpsJson.get("lon").toString()) : 0;
        Double lat = StringUtil.isNumeric(gpsJson.get("lat")) ? Double.parseDouble(gpsJson.get("lat").toString()) : 0;
        if (lon == 0 && lat == 0) {
            log.error("发送GPS经纬度错误 imei={} ", imei);
            return null;
        }
        //设备的国家编码
//        Integer mcc = message.getInteger("mcc");
        if (mcc == null || mcc == 460) {
            // 不传 或者 460 都是中国, 使用高德地图的坐标转换
            Map<String, Object> param = new HashMap<>();
            if (isGdOrBdLBS == 1) {
                param.put("key", keyProperties.getAmapWebKey().get(RandomUtil.getRandomInt(0, keyProperties.getAmapWebKey().size())));
                param.put("coordsys", "gps");
                param.put("locations", lon + "," + lat);
                String convertURL = "http://restapi.amap.com/v3/assistant/coordinate/convert?" + HttpReq.paramJoint(param);
                String response = HttpReq.sendGet(convertURL);
                log.info("imei:{} 高德地图的GPS坐标转换 url = {}.  response = {}", imei, convertURL, response);
                if (StringUtil.isNotBlank(response)) {
                    JSONObject respJson = JSON.parseObject(response);
                    if (INFO_CODE.equals(respJson.get("infocode"))) {
                        if (StringUtil.isNotBlank(respJson.getString("locations"))) {
                            String[] locations = respJson.getString("locations").split(",");
                            if (locations.length >= 2) {
                                if (StringUtil.isNotBlank(locations[0])) {
                                    lon = Double.parseDouble(locations[0]);
                                }
                                if (StringUtil.isNotBlank(locations[1])) {
                                    lat = Double.parseDouble(locations[1]);
                                }
                                if (lon == 0 && lat == 0) {
                                    log.error("发送GPS经纬度错误 imei={} ", imei);
                                    return null;
                                }
                            } else {
                                log.error("imei={} locations is Exception. respJson={}", imei, respJson);
                                return null;
                            }
                        }
                    } else {
                        log.error("GPS坐标转换失败,请求高德接口问题 imei={} ", imei);
                        return null;
                    }
                } else {
                    log.error("GPS坐标转换失败,请求高德接口问题 imei={} ", imei);
                    return null;
                }
            } else {
                param.put("ak", keyProperties.getAmapBdWebLBSKey().get(RandomUtil.getRandomInt(0, keyProperties.getAmapBdWebLBSKey().size())));
                param.put("model", "2");
                param.put("coords", lon + "," + lat);
                String convertURL = "https://api.map.baidu.com/geoconv/v2/?";
                JSONObject respJson = HttpReq.sendBdGet(convertURL, param);
                if (ObjectUtils.isNotEmpty(respJson)) {
                    if (respJson.getInteger("status").equals(0)) {
                        if (StringUtil.isNotBlank(respJson.getString("locations"))) {
                            JSONArray result = respJson.getJSONArray("result");
                            if (ObjectUtils.isNotEmpty(result)) {
                                JSONObject locations = result.getJSONObject(0);
                                if (StringUtil.isNotBlank(locations.getDouble("x"))) {
                                    lon = locations.getDouble("x");
                                }
                                if (StringUtil.isNotBlank(locations.getDouble("y"))) {
                                    lat = locations.getDouble("y");
                                }
                                if (lon == 0 && lat == 0) {
                                    log.error("发送GPS经纬度错误 imei={} ", imei);
                                    return null;
                                }
                            } else {
                                log.error("imei={} locations is Exception. respJson={}", imei, respJson);
                                return null;
                            }
                        }
                    } else {
                        log.error("GPS坐标转换失败,请求百度接口问题 imei={} ", imei);
                        return null;
                    }
                } else {
                    log.error("GPS坐标转换失败,请求百度接口问题 imei={} ", imei);
                    return null;
                }
            }

        } else {
            //其他的默认海外
            log.info("imei:{} 海外GPS坐标 使用WGS84坐标系：即地球坐标系，国际上通用的坐标系。无效转换", imei);
        }

        JSONObject result = new JSONObject();
        result.put("lon", lon);
        result.put("lat", lat);
        return result;
    }


    /**
     * 维智 LBS 解析
     *
     * @param message
     * @param device
     * @param resultJson 返回的结果
     * @time: 2/14/22 6:36 PM
     * @author: Weichao
     * @version 1.0
     */
    private void toWZ_LBSparsing(JSONObject message, Device device, JSONObject resultJson) {
        JSONArray cells = message.getJSONArray("baseStation");
        Integer cdma = message.getInteger("baseStationType");
        JSONArray wifis = message.getJSONArray("wifi");
        String imei = device.getId();

        //维智定位的可能会存在超时连接不上的问题,需要捕获超时问题
        String url = "https://api.newayz.com/location/hub/v1/track_points?access_key=" + wayzKey;
        JSONObject param = new JSONObject();
        param.put("timestamp", System.currentTimeMillis());
        param.put("id", UUIDGenerator.generate());
        JSONObject assertParam = new JSONObject();
        assertParam.put("id", imei);
        assertParam.put("manufacturer", "Note");
        assertParam.put("model", "Note");
        assertParam.put("imeiMd5", DigestUtils.md5Hex(imei).toUpperCase());
        assertParam.put("uniqueId", imei);
        param.put("asset", assertParam);
        //处理成 符合 WAYZ的 基站数据
        List<Object> cellular = new ArrayList<>();
        List<Object> wayzWifis = new ArrayList<>();
        if (cdma == 1 && cells != null && cells.size() >= 1) {
            JSONObject cell = cells.getJSONObject(0);
            if (cell.get("sid") != null && !"0".equals(cell.get("sid").toString())
                    && cell.get("nid") != null && !"0".equals(cell.get("nid").toString())
                    && cell.get("bid") != null && !"0".equals(cell.get("bid").toString())) {
                JSONObject temp = new JSONObject();
                temp.put("connected", true);
                temp.put("cellId", new Double(cell.get("bid").toString()).intValue());
                //移动网号, 中国移动:0;中国联通:1
                temp.put("mobileNetworkCode", new Double(cell.get("sid").toString()).intValue());
                //位置区域码, 取值范围:0-65535
                temp.put("locationAreaCode", new Double(cell.get("nid").toString()).intValue());
                //基站的网络类型
                temp.put("radioType", "cdma");
                //用户所属国家代码
                temp.put("mobileCountryCode", 460);
                cellular.add(temp);
            }
        } else if (cells != null && cells.size() > 0) {
            for (int i = 0; i < cells.size(); i++) {
                JSONObject temp = new JSONObject();
                JSONObject cell = cells.getJSONObject(i);
                if ((cell.get("connected") != null && "1".equals(cell.get("connected").toString())) || i == 0) {
                    temp.put("connected", true);
                }
                if (StringUtil.isNumeric(cell.get("timestamp"))) {
                    temp.put("timestamp", Long.parseLong(cell.get("timestamp").toString()) * 1000);
                } else {
                    temp.put("timestamp", System.currentTimeMillis());
                }
                //小区ID
                int cellId = StringUtil.isNumeric(cell.get("ci")) ? new Double(cell.get("ci").toString()).intValue() : 0;
                if (cellId == 0) {
                    // cellId 为 0 的为无效数据, 丢弃
                    continue;
                }
                temp.put("cellId", cellId);
                //基站的信号 的绝对值
                temp.put("signalStrength", StringUtil.isNumeric(cell.get("rxlev")) ? Math.abs(new Double(cell.get("rxlev").toString()).intValue()) : 0);
                String radioType = "gsm";
                //如果大于 cell["ci"] > 65535 为3G 基站 (WCDMA)
                if (cellId > 65535) {
                    radioType = "wcdma";
                }
                if (cells.size() == 1) {
                    //如果只有一个基站,那么是 4G 基站
                    radioType = "lte";
                }
                if (StringUtil.isNotBlank(cell.get("type"))) {
                    radioType = cell.getString("type");
                }
                //基站的网络类型
                temp.put("radioType", radioType);
                //用户所属国家代码
                //识别不到默认中国
                Integer mcc = 460;
                if (cell.containsKey("mcc")) {
                    mcc = Integer.valueOf(cell.get("mcc").toString());
                    if (mcc == 0) {
                        mcc = 460;
                    }
                }
                temp.put("mobileCountryCode", mcc);
                //移动网号, 中国移动:0;中国联通:1
                temp.put("mobileNetworkCode", cell.get("mnc"));
                //位置区域码, 取值范围:0-65535
                temp.put("locationAreaCode", cell.get("lac"));
                cellular.add(temp);
            }
        }

        if (wifis != null && wifis.size() > 0) {
            for (int i = 0; i < wifis.size(); i++) {
                JSONObject temp = new JSONObject();
                JSONObject wifi = wifis.getJSONObject(i);
                if (StringUtil.isNotBlank(wifi.get("connected")) && "1".equals(wifi.get("connected").toString())) {
                    temp.put("connected", true);
                }
                if (wifi.containsKey("channel")) {
                    if (StringUtil.isNumeric(wifi.get("channel"))) {
                        temp.put("channel", new Double(wifi.get("channel").toString()).intValue());
                    } else {
                        temp.put("channel", 0);
                    }
                }
                if (wifi.containsKey("frequency")) {
                    if (StringUtil.isNumeric(wifi.get("frequency"))) {
                        temp.put("frequency", new Double(wifi.get("frequency").toString()).intValue());
                    } else {
                        temp.put("frequency", 0);
                    }
                }
                if (StringUtil.isNotBlank(wifi.get("mac"))) {
                    temp.put("macAddress", wifi.getString("mac").replace("-", ":"));
                }
                if (StringUtil.isNumeric(wifi.get("signal"))) {
                    temp.put("signalStrength", Math.abs(Integer.parseInt(wifi.getString("signal"))));
                }
                wayzWifis.add(temp);
            }
        }
        JSONObject locationParam = new JSONObject();
        locationParam.put("cellulars", cellular);
        locationParam.put("wifis", wayzWifis);
        param.put("location", locationParam);
        //维智定位请求
        String responseRes = null;
        log.info("维智定位开始请求,请求imei:{}", imei);
        try {
            responseRes = HttpReq.sendLBSLocatePostJson(url, param.toJSONString());
        } catch (Exception e) {
            log.error("imei:" + imei + " 使用维智定位异常则使用高德定位", e);
//                e.printStackTrace();
        }
//        log.info("维智定位url={} param={}, responseRes={}", url, param, responseRes);

        //定位的层级
        int level = 0;
        //转换成和高德差不多一样的数据结构
        JSONObject tempAmapResDict = new JSONObject();
        if (StringUtil.isNotBlank(responseRes)) {
            JSONObject responseJson = JSON.parseObject(responseRes);
            tempAmapResDict.put("status", "1");
            tempAmapResDict.put("info", "OK");
            tempAmapResDict.put("infocode", INFO_CODE);
            JSONObject tempResult = new JSONObject();
            String locationType = "LBS";
            if (responseJson.containsKey("location")) {
                JSONObject location = responseJson.getJSONObject("location");
                if (location.containsKey("address")) {
                    JSONObject address = location.getJSONObject("address");
                    if (StringUtil.isNumeric(address.get("level"))) {
                        level = new Double(address.getString("level")).intValue();
                    }
                    tempResult.put("desc", address.get("name"));
                    tempResult.put("city", "");
                    tempResult.put("adcode", "");
                    if (address.containsKey("context")) {
                        JSONArray context = address.getJSONArray("context");
                        for (int i = 1; i < context.size(); i++) {
                            if ("City".equals(context.getJSONObject(i).get("type"))
                                    || "District".equals(context.getJSONObject(i).get("type"))) {
                                tempResult.put("city", context.getJSONObject(i).get("name"));
                                tempResult.put("adcode", context.getJSONObject(i).get("code"));
                            }
                        }
                    }
                }
                if (location.containsKey("position")) {
                    JSONObject position = location.getJSONObject("position");
                    if (position.containsKey("point")) {
                        JSONObject point = position.getJSONObject("point");
                        tempResult.put("location", point.get("longitude") + "," + point.get("latitude"));
                        tempResult.put("altitude", String.valueOf(point.get("altitude")));
                    }
                    tempResult.put("radius", StringUtil.isBlank(position.get("accuracy")) ? "0" : String.valueOf(position.get("accuracy")));
                    String source = position.getString("source");
                    if ("cell".equals(source)) {
                        locationType = "LBS";
                    } else {
                        //其他全部归为 WIFI (智能定位)
                        locationType = "WIFI";
                    }
                }
            }
            log.info("维智定位开始请求,请求成功 imei:{} 经纬度:{}", imei, tempResult.getString("location"));
            tempAmapResDict.put("result", tempResult);
            setReturnLBSDataWithImei(imei, tempAmapResDict, false);
            resultJson.put("locationType", locationType);
            if (level >= 14) {
                //只有室内定位才使用后台提供的位置
                resultJson.put("useBgAddressTips", 1);
            } else {
                resultJson.put("useBgAddressTips", 0);
            }
            resultJson.put("data", tempAmapResDict);
        } else {
            //请求维智接口 无返回结果
            //失败没有data
            //可判断resultJson 如果没有data 使用高德定位重新获取位置
            //useGaoDe = true;
        }
    }

    /**
     * 高德 LBS 解析
     *
     * @param message
     * @param device
     * @param resultJson 返回的结果
     * @time: 2/14/22 6:36 PM
     * @author: Weichao
     * @version 1.0
     */
    private void toGD_LBSparsing(JSONObject message, Device device, JSONObject resultJson) {
        JSONArray cells = message.getJSONArray("baseStation");
        String cdma = message.getString("baseStationType");
        JSONArray wifis = message.getJSONArray("wifi");
        String imei = device.getId();

        Map<String, Object> param = new HashMap<>();
        param.put("key", listDataProperties.getAmapLBSKey().get(RandomUtil.getRandomInt(0, listDataProperties.getAmapLBSKey().size())));
        param.put("network", "GSM");
        String locationType = "LBS";
        //优先级 WIFI > CELLS > TELECOMCELL
        if ("1".equals(cdma) && cells != null && cells.size() > 0) {
            JSONObject cell = cells.getJSONObject(0);
            if (cell != null && cell.size() >= 3) {
                //CDMA 格式为:sid,nid,bid,lon,lat,signal， 其中 lon,lat, signal 可为空，格式为:sid,nid,bid,,,signal
                param.put("accesstype", "0");
                cdma = "1";
                String sid = cell.getString("sid");
                String nid = cell.getString("nid");
                String bid = cell.getString("bid");
                if (!"0".equals(sid) && !"0".equals(nid) && !"0".equals(bid)) {
                    StringBuilder bts = new StringBuilder();
                    bts.append(sid).append(",").append(nid).append(",").append(bid).append(",");
                    if (cell.containsKey("lon")) {
                        bts.append(cell.get("lon"));
                    }
                    bts.append(",");
                    if (cell.containsKey("lat")) {
                        bts.append(cell.get("lat"));
                    }
                    bts.append(",");
                    if (cell.containsKey("signal")) {
                        bts.append(cell.get("signal"));
                    }
                    param.put("bts", bts.toString());
                    locationType = "LBS";
                }
            }
        }
        if (cells != null && cells.size() > 0) {
            //非 CDMA 格式为:mcc,mnc,lac,cellid,signal
            if (cells.size() == 1) {
                //如果只上传了一个基站,那么应该是 4G基站, 使用LTE
                param.put("network", "LTE");
            }
            param.put("accesstype", "0");
            JSONObject cellIndex1 = cells.getJSONObject(0);
            int rxlev1 = TCPUtil.rxlevHandle(cellIndex1.get("rxlev"));
            cellIndex1.put("rxlev", rxlev1);
            rxlev1 = Math.abs(rxlev1);
            //用户所属国家代码
            //识别不到默认中国
            Integer mcc = 460;
            if (cellIndex1.containsKey("mcc")) {
                mcc = Integer.valueOf(cellIndex1.get("mcc").toString());
            }
            param.put("bts", String.format("%s,%s,%s,%s,%s", mcc, cellIndex1.get("mnc"),
                    cellIndex1.get("lac"), cellIndex1.get("ci"), cellIndex1.get("rxlev")));
            cdma = "0";
            if (cells.size() > 1) {
                StringBuilder nearbtsStr = new StringBuilder();
                for (int i = 0; i < cells.size(); i++) {
                    JSONObject ce = cells.getJSONObject(i);
                    int rx = TCPUtil.rxlevHandle(ce.get("rxlev"));
                    ce.put("rxlev", rx);
                    //用户所属国家代码
                    Integer tempMcc = Integer.valueOf(ce.get("mcc").toString());
                    if (tempMcc == 0) {
                        //识别不到默认中国
                        tempMcc = 460;
                    }
                    if (Math.abs(rx) < rxlev1) {
                        rxlev1 = Math.abs(rx);
                        param.put("bts", String.format("%s,%s,%s,%s,%s", tempMcc, ce.get("mnc"),
                                ce.get("lac"), ce.get("ci"), ce.get("rxlev")));
                    }
                    nearbtsStr.append(String.format("%s,%s,%s,%s,%s|", tempMcc, ce.get("mnc"),
                            ce.get("lac"), ce.get("ci"), ce.get("rxlev")));
                }
                try {
                    param.put("nearbts", URLEncoder.encode(nearbtsStr.substring(0, nearbtsStr.length() - 1), "UTF-8"));
                } catch (Exception e) {
                    e.printStackTrace();
                }
                locationType = "LBS";
            }
        }
        if (wifis != null && wifis.size() > 0) {
            param.put("accesstype", "1");
            StringBuilder wifiStr = new StringBuilder();
            if (filterWifiVendorModelService != null) {
                FilterWifiVendorModel vendorModel = filterWifiVendorModelService.findById("mianConfig");
                for (int i = 0; i < wifis.size(); i++) {
                    JSONObject wifi = wifis.getJSONObject(i);
                    if (StringUtil.isNotBlank(wifi.getString("mac"))) {
                        String macBefore6 = wifi.getString("mac").replace(":", "");
                        if (macBefore6.length() > 6) {
                            macBefore6 = macBefore6.substring(0, 6).toUpperCase();
                            MacToCompany macToCompany = macToCompanyService.findById(macBefore6);
                            if (macToCompany != null && StringUtil.isNotBlank(macToCompany.getCompanyName())) {
                                String companyName = macToCompany.getCompanyName().toUpperCase();
                                boolean isFilter = false;
                                for (String moblieType : vendorModel.getMoblieType()) {
                                    if (companyName.contains(moblieType)) {
                                        // 公司名中存在数据库中存的过滤的手机厂商的名字的 跳过本轮
                                        log.info("Mac 地址被过滤 = {} 过滤的厂商是={}", wifi.get("mac"), moblieType);
                                        isFilter = true;
                                        break;
                                    }
                                }
                                if (isFilter) {
                                    continue;
                                }
                            }
                        }
                    }
                    int newSignal = 0;
                    if (StringUtil.isNumeric(wifi.get("signal"))) {
                        newSignal = new Double(wifi.getString("signal")).intValue();
                        if (newSignal > 0) {
                            newSignal = 2 * newSignal - 113;
                        }
                    }
                    if (StringUtil.isNotBlank(wifi.get("connected")) && "1".equals(wifi.getString("connected"))) {
                        param.put("mmac", String.format("%s,%s,%s", wifi.get("mac"), newSignal, wifi.get("ssid")));
                    }
                    wifiStr.append(String.format("%s,%s,%s|", wifi.get("mac"), newSignal, wifi.get("ssid")));
                }
            }

            if (wifiStr.length() > 0) {
                try {
                    param.put("macs", URLEncoder.encode(wifiStr.substring(0, wifiStr.length() - 1), "UTF-8"));
                } catch (Exception e) {
                    e.printStackTrace();
                }
                locationType = "WIFI";
            }
        }
        param.put("imei", imei);
        param.put("cdma", cdma);
        String imsi = RedisAccountClusterService.getJedisCluster().hget(imei, ConstCacheField.IMSI);
        if (StringUtil.isNotBlank(imsi)) {
            param.put("imsi", imsi);
        }
        long GDstartFlagTime = System.currentTimeMillis();
        String requestURL = "http://apilocate.amap.com/position?" + HttpReq.paramJoint(param);
        String responseStr = HttpReq.sendLBSLocateGet(requestURL);
        JSONObject responseJson = new JSONObject();
        if (StringUtil.isNotBlank(responseStr)) {
            log.info("imei: {} 高德地图位置解析 time={} kafkaModelType;{}", imei, System.currentTimeMillis() - GDstartFlagTime, message.getInteger("kafkaModelType"));
            responseJson = JSON.parseObject(responseStr);
            setReturnLBSDataWithImei(imei, responseJson, false);
            resultJson.put("locationType", locationType);
            resultJson.put("useBgAddressTips", 0);
            resultJson.put("data", responseJson);
            log.info("imei:{} 基站WIFI 高德地图位置解析 resultJson: {}", imei, resultJson);
        } else {
            //失败没有data
        }

    }


    /***
     * @description 百度LSB解析，模仿上面高德定位所写
     * @author cwj
     * @param [message, device, resultJson]
     * @return void
     * @date @time 2024/10/14 14:28
     **/
    private void toBD_LBSparsing(JSONObject message, Device device, JSONObject resultJson) {
        JSONArray cells = message.getJSONArray("baseStation");
        String cdma = message.getString("baseStationType");
        JSONArray wifis = message.getJSONArray("wifi");
        String imei = device.getId();

        JSONObject param = new JSONObject();
        String ak = listDataProperties.getAmapBdWebLBSKey().get(RandomUtil.getRandomInt(0, listDataProperties.getAmapBdWebLBSKey().size()));
        //初始化请求参数的基础参数
        initRequestParam(param, ak);
        List<JSONObject> body = new ArrayList<>();
        JSONObject paramObj = new JSONObject();
        String locationType = "LBS";
        //优先级 WIFI > CELLS > TELECOMCELL
        if ("1".equals(cdma) && cells != null && cells.size() > 0) {
            JSONObject cell = cells.getJSONObject(0);
            if (cell != null && cell.size() >= 3) {
                //CDMA 格式为:sid,nid,bid,lon,lat,signal， 其中 lon,lat, signal 可为空，格式为:sid,nid,bid,,,signal
                paramObj.put("accesstype", 0);
                cdma = "1";
                String sid = cell.getString("sid");
                String nid = cell.getString("nid");
                String bid = cell.getString("bid");
                if (!"0".equals(sid) && !"0".equals(nid) && !"0".equals(bid)) {
                    StringBuilder bts = new StringBuilder();
                    bts.append(sid).append(",").append(nid).append(",").append(bid).append(",");
                    if (cell.containsKey("lon")) {
                        bts.append(cell.get("lon"));
                    }
                    bts.append(",");
                    if (cell.containsKey("lat")) {
                        bts.append(cell.get("lat"));
                    }
                    bts.append(",");
                    if (cell.containsKey("signal")) {
                        bts.append(cell.get("signal"));
                    }
                    paramObj.put("bts", bts.toString());
                    locationType = "LBS";
                }
            }
        }
        if (cells != null && cells.size() > 0) {
            //非 CDMA 格式为:mcc,mnc,lac,cellid,signal
            if (cells.size() == 1) {
                //如果只上传了一个基站,那么应该是 4G基站, 使用LTE
                paramObj.put("network", "LTE");
            }
            paramObj.put("accesstype", 0);
            JSONObject cellIndex1 = cells.getJSONObject(0);
            int rxlev1 = TCPUtil.rxlevHandle(cellIndex1.get("rxlev"));
            cellIndex1.put("rxlev", rxlev1);
            rxlev1 = Math.abs(rxlev1);
            //用户所属国家代码
            //识别不到默认中国
            Integer mcc = 460;
            if (cellIndex1.containsKey("mcc")) {
                mcc = Integer.valueOf(cellIndex1.get("mcc").toString());
            }
            paramObj.put("bts", String.format("%s,%s,%s,%s,%s", mcc, cellIndex1.get("mnc"),
                    cellIndex1.get("lac"), cellIndex1.get("ci"), cellIndex1.get("rxlev")));
            cdma = "0";
            if (cells.size() > 1) {
                StringBuilder nearbtsStr = new StringBuilder();
                for (int i = 0; i < cells.size(); i++) {
                    JSONObject ce = cells.getJSONObject(i);
                    int rx = TCPUtil.rxlevHandle(ce.get("rxlev"));
                    ce.put("rxlev", rx);
                    //用户所属国家代码
                    Integer tempMcc = Integer.valueOf(ce.get("mcc").toString());
                    if (tempMcc == 0) {
                        //识别不到默认中国
                        tempMcc = 460;
                    }
                    if (Math.abs(rx) < rxlev1) {
                        rxlev1 = Math.abs(rx);
                        paramObj.put("bts", String.format("%s,%s,%s,%s,%s", tempMcc, ce.get("mnc"),
                                ce.get("lac"), ce.get("ci"), ce.get("rxlev")));
                    }
                    nearbtsStr.append(String.format("%s,%s,%s,%s,%s|", tempMcc, ce.get("mnc"),
                            ce.get("lac"), ce.get("ci"), ce.get("rxlev")));
                }
                try {
                    //log.info("nearbts数据为："+nearbtsStr.substring(0, nearbtsStr.length() - 1));
                    //paramObj.put("nearbts", URLEncoder.encode(nearbtsStr.substring(0, nearbtsStr.length() - 1), "UTF-8"));
                    paramObj.put("nearbts", nearbtsStr.substring(0, nearbtsStr.length() - 1));
                } catch (Exception e) {
                    log.error("百度地图LBS解析出现异常，异常信息为：{}", e.getMessage());
                }
                locationType = "LBS";
            }
        }
        if (wifis != null && wifis.size() > 0) {
            paramObj.put("accesstype", 1);
            StringBuilder wifiStr = new StringBuilder();
            if (filterWifiVendorModelService != null) {
                FilterWifiVendorModel vendorModel = filterWifiVendorModelService.findById("mianConfig");
                for (int i = 0; i < wifis.size(); i++) {
                    JSONObject wifi = wifis.getJSONObject(i);
                    if (StringUtil.isNotBlank(wifi.getString("mac"))) {
                        String macBefore6 = wifi.getString("mac").replace(":", "");
                        if (macBefore6.length() > 6) {
                            macBefore6 = macBefore6.substring(0, 6).toUpperCase();
                            MacToCompany macToCompany = macToCompanyService.findById(macBefore6);
                            if (macToCompany != null && StringUtil.isNotBlank(macToCompany.getCompanyName())) {
                                String companyName = macToCompany.getCompanyName().toUpperCase();
                                boolean isFilter = false;
                                for (String moblieType : vendorModel.getMoblieType()) {
                                    if (companyName.contains(moblieType)) {
                                        // 公司名中存在数据库中存的过滤的手机厂商的名字的,跳过本轮
                                        log.info("Mac 地址被过滤 = {} 过滤的厂商是={}", wifi.get("mac"), moblieType);
                                        isFilter = true;
                                        break;
                                    }
                                }
                                if (isFilter) {
                                    continue;
                                }
                            }
                        }
                    }
                    int newSignal = 0;
                    if (StringUtil.isNumeric(wifi.get("signal"))) {
                        newSignal = new Double(wifi.getString("signal")).intValue();
                        if (newSignal > 0) {
                            newSignal = 2 * newSignal - 113;
                        }
                    }
                    if (StringUtil.isNotBlank(wifi.get("connected")) && "1".equals(wifi.getString("connected"))) {
                        paramObj.put("mmac", String.format("%s,%s,%s", wifi.get("mac"), newSignal, wifi.get("ssid")));
                    }
                    wifiStr.append(String.format("%s,%s,%s|", wifi.get("mac"), newSignal, wifi.get("ssid")));
                }
            }
            if (wifiStr.length() > 0) {
                try {
                    //log.info("macs数据为："+wifiStr.substring(0, wifiStr.length() - 1));
                    //paramObj.put("macs", URLEncoder.encode(wifiStr.substring(0, wifiStr.length() - 1), "UTF-8"));
                    paramObj.put("macs", wifiStr.substring(0, wifiStr.length() - 1));
                } catch (Exception e) {
                    log.error("百度地图定位发生异常，异常信息为：{}", e.getMessage());
                }
                locationType = "WIFI";
            }
        }
        //设置body参数的内容
        settingBodyParam(paramObj, imei, cdma);
        String imsi = RedisAccountClusterService.getJedisCluster().hget(imei, ConstCacheField.IMSI);
        if (StringUtil.isNotBlank(imsi)) {
            paramObj.put("imsi", imsi);
        }
        body.add(paramObj);
        param.put("body", body);
        long BDstartFlagTime = System.currentTimeMillis();
        String requestURL = "https://api.map.baidu.com/locapi/v2";
        try {
            //log.info("百度地图请求参数:{}",param);
            JSONObject responseJsonObject = HttpReq.sendBdLBSLocatePost(requestURL, param);
            //保存或更新定位原始数据，且进行了百度地图解析的数据（每天只保存一条，三天过期）
            //deviceBdLocationDataService.saveOrUpdateBdLoc(imei,message,param,responseJsonObject);
            //保存或更新定位原始数据，且进行了百度地图解析的数据（定位了就保存，三天过期）
            deviceBdLocationDataService.saveBdLocData(imei, message, param, responseJsonObject);
            if (ObjectUtils.isNotEmpty(responseJsonObject) && responseJsonObject.getInteger("errcode").equals(0)) {
                log.info("imei: {} 百度地图位置解析 time={} kafkaModelType;{}", imei, System.currentTimeMillis() - BDstartFlagTime, message.getInteger("kafkaModelType"));
                JSONArray bodyArray = responseJsonObject.getJSONArray("body");
                JSONObject bodyObj = bodyArray.getJSONObject(0);
                //查询百度地图对应的adcode编码
                Map<String, Object> paramMap = new LinkedHashMap<>();
                String[] loactions = bodyObj.getString("location").split(",");
                //设置逆编码请求参数
                settingReserseGeocodingParam(paramMap, ak, loactions);
                JSONObject inverAddrJsonObj = HttpReq.sendBdGet("https://api.map.baidu.com/reverse_geocoding/v3?", paramMap);
                if (ObjectUtils.isNotEmpty(inverAddrJsonObj) && inverAddrJsonObj.getInteger("status").equals(0)) {
                    //组装百度定位的数据，跟之前高德地图定位返回值保持一致
                    assembleResultJson(resultJson, bodyObj, inverAddrJsonObj, imei, locationType);
                    String logStr = StringTool.subStringByLength(resultJson.toJSONString(), 100);
                    log.info("imei:{} 基站WIFI 百度地图位置解析 resultJson: {}", imei, logStr);
                } else {
                    //设置百度地址解析失败后返回的数据
                    assembleFailJsonData(resultJson, responseJsonObject, locationType);
                }
            } else {
                //设置百度地址解析失败后返回的数据
                assembleFailJsonData(resultJson, responseJsonObject, locationType);
            }
        } catch (Exception e) {
            log.error("百度地图定位发生异常，异常信息为：{}", e.getMessage());
        }
    }

    /**
     * @param [resultJson, responseJsonObject, locationType]
     * @return void
     * @description 设置百度地址解析失败后返回的数据
     * @author cwj
     * @date @time 2024/10/16 16:27
     **/
    private static void assembleFailJsonData(JSONObject resultJson, JSONObject responseJsonObject, String locationType) {
        JSONObject responseJson = new JSONObject();
        responseJson.put("infocode", responseJsonObject.getString("errcode"));
        responseJson.put("info", "FAIL");
        responseJson.put("status", "0");
        resultJson.put("locationType", locationType);
        resultJson.put("useBgAddressTips", 0);
    }

    /**
     * @param [paramMap, ak, loactions]
     * @return void
     * @description 逆编码请求参数设置
     * @author cwj
     * @date @time 2024/10/16 16:17
     **/
    private static void settingReserseGeocodingParam(Map<String, Object> paramMap, String ak, String[] loactions) {
        paramMap.put("ak", ak);
        paramMap.put("output", "json");
        paramMap.put("coordtype", "gcj02ll");
        paramMap.put("extensions_poi", "0");
        paramMap.put("location", loactions[1] + "," + loactions[0]);
    }

    /**
     * @param [paramObj, imei, cdma]
     * @return void
     * @description body参数内容设置
     * @author cwj
     * @date @time 2024/10/16 16:15
     **/
    private static void settingBodyParam(JSONObject paramObj, String imei, String cdma) {
        paramObj.put("ctime", System.currentTimeMillis() + "");
        paramObj.put("coor", "GCJ02");
        paramObj.put("imei", imei);
        paramObj.put("cdma", Integer.valueOf(cdma));
        paramObj.put("need_rgc", "Y");
        paramObj.put("need_poi", "Y");
        paramObj.put("sema", "aptagd");
    }

    /**
     * @param [param, ak]
     * @return void
     * @description 初始化基础参数
     * @author cwj
     * @date @time 2024/10/16 16:14
     **/
    private static void initRequestParam(JSONObject param, String ak) {
        param.put("key", ak);
        param.put("src", "pinxuan");
        param.put("prod", "pinxuan_api");
        param.put("trace", false);
        param.put("ver", "1.0");
    }

    /**
     * @param [resultJson, bodyObj, inverAddrJsonObj, imei, locationType]
     * @return void
     * @description 装百度定位的数据，跟之前高德地图定位返回值保持一致
     * @author cwj
     * @date @time 2024/10/15 13:59
     **/
    private void assembleResultJson(JSONObject resultJson, JSONObject bodyObj, JSONObject inverAddrJsonObj, String imei, String locationType) {
        JSONObject responseJson = new JSONObject();
        JSONObject resultJsonObj = new JSONObject();
        resultJsonObj.put("country", bodyObj.getString("country"));
        resultJsonObj.put("province", bodyObj.getString("province"));
        resultJsonObj.put("citycode", bodyObj.getString("citycode"));
        resultJsonObj.put("city", bodyObj.getString("city"));
        resultJsonObj.put("road", bodyObj.getString("road"));
        resultJsonObj.put("location", bodyObj.getString("location"));
        resultJsonObj.put("poi", !StringUtils.isEmpty(bodyObj.getString("aptag")) ? bodyObj.getString("aptag").replace("在", "") : "");
        resultJsonObj.put("district", bodyObj.getString("district"));
        resultJsonObj.put("type", bodyObj.getIntValue("type"));
        resultJsonObj.put("radius", bodyObj.getIntValue("radius"));
        resultJsonObj.put("desc", bodyObj.getString("province") + bodyObj.getString("city")
                + bodyObj.getString("district") + bodyObj.getString("road") + resultJsonObj.getString("poi"));
        JSONObject iAResult = inverAddrJsonObj.getJSONObject("result");
        if (ObjectUtils.isNotEmpty(iAResult)) {
            JSONObject addressComponent = iAResult.getJSONObject("addressComponent");
            if (ObjectUtils.isNotEmpty(addressComponent) && StringUtils.isNotEmpty(addressComponent.getString("adcode"))) {
                resultJsonObj.put("adcode", addressComponent.getString("adcode"));
                resultJsonObj.put("street", addressComponent.getString("street"));
            }
        }
        //城市编码在天气查询中用到了，没有再次取值放入
        if (ObjectUtils.isEmpty(resultJsonObj.get("adcode")) && ObjectUtils.isNotEmpty(bodyObj.get("adcode"))) {
            resultJsonObj.put("adcode", bodyObj.getString("adcode"));
        }
        responseJson.put("result", resultJsonObj);
        //缓存定位数据
        setReturnLBSDataWithImei(imei, responseJson, false);
        responseJson.put("infocode", INFO_CODE);
        responseJson.put("info", "OK");
        responseJson.put("status", "1");
        resultJson.put("locationType", locationType);
        resultJson.put("useBgAddressTips", 0);
        resultJson.put("data", responseJson);
        log.info("------百度地址解析后的数据为：{}", resultJson);
    }


    /**
     * 谷歌 LBS 解析 (解析完成对 resultJson 进行拼接成和高德一样返回的数据结构, 只是没有地理位置等信息而已)
     *
     * @param message
     * @param device
     * @param resultJson
     * @time: 2/15/22 9:25 AM
     * @author: Weichao
     * @version 1.0
     */
    private void toGoogle_LBSparsing(JSONObject message, Device device, JSONObject resultJson) {
        //https://www.googleapis.com/geolocation/v1/geolocate?key=
        Integer mcc = message.getInteger("mcc");
        if (mcc == null) {
            mcc = 460;
        }
        String locationType = "LBS";
        Integer mncOrSid = 0;
        String radioType = "gsm";
        CellTower[] cellTowers = googleMapsApi.parseNotCDMACellTowers(message.getJSONArray("cells"));
        if (cellTowers == null) {
            cellTowers = googleMapsApi.parseCDMACellTowers(message.getJSONObject("telecomcell"));
            radioType = "cdma";
            mncOrSid = message.getJSONObject("telecomcell").getInteger("sid");
        } else {
            JSONObject cell = message.getJSONArray("cells").getJSONObject(0);
            String type = cell.getString("type");
            if (type != null) {
                radioType = type;
            }
            mncOrSid = cell.getInteger("mnc");
        }
        WifiAccessPoint[] wifiAccessPoints = googleMapsApi.parseWifiAccessPoints(message.getJSONArray("wifis"));
        if (wifiAccessPoints != null) {
            locationType = "WIFI";
        }
        GeolocationResult geolocationResult = googleMapsApi.geolocation(device.getId(), mcc, mncOrSid, radioType, cellTowers, wifiAccessPoints);
        // 拼接成和高德一样返回的数据结构
        /*
        {
            "data": {
                "result": {
                    "country": "中国",
                    "province": "广东省",
                    "citycode": "0755",
                    "city": "深圳市",
                    "adcode": "440305",
                    "road": "茶光路",
                    "street": "茶光路",
                    "location": "113.9391249,22.5684901",
                    "poi": "波顿科技园",
                    "type": "4",
                    "radius": "550",
                    "desc": "广东省 深圳市 南山区 茶光路 靠近波顿科技园"
                },
                "infocode": "10000",
                "info": "OK",
                "status": "1"
            },
            "locationType": "LBS",
            "useBgAddressTips": 0
        }
        result.type: 定位类型，0：没有得到定位结果；其他数字为：正常获取定位结果

        */
        JSONObject subJson = new JSONObject();
        subJson.put("info", "OK");
        subJson.put("status", "1");
        subJson.put("infocode", INFO_CODE);

        JSONObject result = new JSONObject();
        result.put("type", "0");
        if (geolocationResult != null && geolocationResult.location.lng != 0 && geolocationResult.location.lat != 0) {
            result.put("type", "4");
            //geolocationResult.location.toUrlValue() ==> 纬度,经度  和高德的不一致,得换过来
//            result.put("location", geolocationResult.location.toUrlValue());
            result.put("location", geolocationResult.location.lng + "," + geolocationResult.location.lat);
            result.put("radius", "" + (int) geolocationResult.accuracy);
            subJson.put("result", result);
            /* Google 解析后拼接完成的数据
            {
                "result": {
                    "location": "113.9391249,22.5684901",
                    "type": "4",
                    "radius": "550"
                },
                "infocode": "10000",
                "info": "OK",
                "status": "1"
            }
            */
            setReturnLBSDataWithImei(device.getId(), subJson, true);
            resultJson.put("data", subJson);
        } else {
            //失败没有data
        }
        resultJson.put("locationType", locationType);
        resultJson.put("useBgAddressTips", 0); //APP端根据经纬度解析地址
    }

    /**
     * 发送反馈指令
     *
     * @param imei
     * @param dataDict
     * @param message
     * @param isGSP
     * @time: 2021/1/21 10:12
     * @author: Rock
     * @version 1.0
     */
    private void feedbackFunc(String imei, JSONObject dataDict, JSONObject message, boolean isGSP) {
        JSONObject kafkaData = new JSONObject();
        kafkaData.put("ident", Integer.parseInt(RandomUtil.getRandom(100000, 899999)));
        if (StringUtil.isNotBlank(message.get("ident"))) {
            kafkaData.put("loc_ident", message.get("ident"));
        }
        if (StringUtil.isNotBlank(message.get("vender"))) {
            kafkaData.put("vender", message.get("vender"));
        }
        //是否是普通定位,如果是普通定位, 无论如何,需要返回反馈的都反馈成功
        if (message.getInteger("kafkaModelType") == 0) {
            kafkaData.put("status", 1);
        } else if (dataDict == null || dataDict.size() == 0) {
            kafkaData.put("status", 0);
        } else {
            kafkaData.put("status", 1);
            if (isGSP) {
                JSONObject gps = message.getJSONObject("gps");
                if (gps != null) {
                    if (StringUtil.isNumeric(gps.get("lon"))) {
                        kafkaData.put("loc_lon", Double.parseDouble(gps.get("lon").toString()));
                    }
                    if (StringUtil.isNumeric(gps.get("lat"))) {
                        kafkaData.put("loc_lat", Double.parseDouble(gps.get("lat").toString()));
                    }
                }
            } else {
                if (StringUtil.isNotBlank(dataDict.getString("location"))) {
                    String[] locations = dataDict.getString("location").split(",");
                    if (locations.length >= 2) {
                        if (StringUtil.isNotBlank(locations[0])) {
                            kafkaData.put("loc_lon", Double.parseDouble(locations[0]));
                        }
                        if (StringUtil.isNotBlank(locations[1])) {
                            kafkaData.put("loc_lat", Double.parseDouble(locations[1]));
                        }
                    } else {
                        log.error("locations is Exception. dataDict={}", dataDict);
                    }
                }
            }
        }
        if (dataDict != null) {
            // 获取json 嵌套2层内的adcode
            for (String key : dataDict.keySet()) {
                Object val = dataDict.get(key);
                if (key.equals("adcode")) {
                    kafkaData.put("adcode", val);
                } else if (val instanceof JSONObject) {
                    for (String subKey : ((JSONObject) val).keySet()) {
                        if (subKey.equals("adcode")) {
                            Object subVal = ((JSONObject) val).get(subKey);
                            kafkaData.put("adcode", subVal);
                        }
                    }
                }

            }
        }
        //发送kafka指令
        kafkaService.emitDeviceDemand(imei, ConstKafka.DEVICE_TYPE_Feedback, kafkaData, null);
    }


    /**
     * 国内高德/百度天气预报
     *
     * @param imei
     * @param adcode
     * @return 天气预报请求 结果: 0:正常 1:天气接口请求失败 1000:获取到的天气时间不正常,可能是adcode不正确
     * @time: 2021/9/22 16:24
     * @author: weichao
     * @version 1.0
     */
    private Integer queryWeatherAndSendMsg(String imei, String adcode, String extensions, JSONObject kafkaData) {
        Integer x;
        if (gdBdConfig.getIsBdOrGd() == 1) {
            x = queryGdMapWeatherAndSendMsg(imei, adcode, extensions, kafkaData);
        } else {
            //extensions = "now";
            //用all查询，才能查出当天的最低温和最高温
            extensions = "all";
            x = queryBdMapWeatherAndSendMsg(imei, adcode, extensions, kafkaData);
        }
        if (x != null) return x;
        return 0;
    }

    /**
     * @param [imei, adcode, extensions, kafkaData]
     * @return java.lang.Integer
     * @description 查询高德地图天气预报并发送kafka消息
     * @author cwj
     * @date @time 2024/10/18 11:25
     **/
    private Integer queryGdMapWeatherAndSendMsg(String imei, String adcode, String extensions, JSONObject kafkaData) {
        //随机获取个高德的Web 服务 的Key (能调用天气预报请求接口)
        String key = listDataProperties.getAmapWebKey().get(RandomUtil.getRandomInt(0, listDataProperties.getAmapWebKey().size()));
        // base 实况天气预报
        String url = String.format("https://restapi.amap.com/v3/weather/weatherInfo?key=%s&city=%s&extensions=%s", key, adcode, extensions);
        String responseStr = HttpReq.sendGet(url, null);
        if (StringUtil.isNotBlank(responseStr)) {
            JSONObject responseObject = JSONObject.parseObject(responseStr);
            //infocode
            String infocode = responseObject.getString("infocode");
            if (Integer.valueOf(infocode) != 10000) {
                String infoStr = responseObject.getString("info");
                int errorCode = 0;
                if ("DAILY_QUERY_OVER_LIMIT".equals(infoStr)) {
                    errorCode = 10001;
                }
                kafkaData.put("errorCode", errorCode);
                return 1;
            }
            JSONArray lives = responseObject.getJSONArray("lives");
            JSONArray forecasts = responseObject.getJSONArray("forecasts");
            JSONObject cacheWJONSOB = new JSONObject();
            if (lives != null && lives.get(0) instanceof JSONObject) {
                //实时天气
                JSONObject weatherJson = lives.getJSONObject(0);
                //把天气预报和adcode 写到redis中
                long reporttime = System.currentTimeMillis(); //没有拿个当前时间默认的
                if (StringUtil.isNotBlank(weatherJson.get("reporttime"))) {
                    //取天气预报的生成时间 此处已被转为毫秒
                    reporttime = DateUtil.strToTimestamp(weatherJson.get("reporttime").toString(), DateUtil.FORMAT_1);
                }
                if (Math.abs(System.currentTimeMillis() - reporttime) > 60 * 60 * 24 * 3 * 1000) {
                    // 获取到的天气时间比 当前时间 晚了 3天, 表明可能是adcode 区域编码不正常
                    log.error("获取到的天气时间比 当前时间 晚了 3天, 表明可能是adcode 区域编码不正常, imei:{} url:{}", imei, url);
//                    return 1000;
                }
                String weatherStr = String.format("%s,%s,%s度,%s风,%s级,%s更新",
                        weatherJson.get("city"),
                        weatherJson.get("weather"),
                        weatherJson.get("temperature"),
                        weatherJson.get("winddirection"),
                        weatherJson.get("windpower"),
                        weatherJson.get("reporttime")
                );
                weatherJson.put("weather_str", weatherStr);
                cacheWJONSOB.put("time", reporttime);
                cacheWJONSOB.put("weatherCacheMapStr", weatherJson.toJSONString());
                kafkaData.putAll(weatherJson);
//                if (StringUtil.isNotBlank(weatherStr)) {
//
//                    cacheWJONSOB.put("time", reporttime);
//                    cacheWJONSOB.put("weatherStr", weatherStr);
//
//                    kafkaData.put("weather", weatherStr);
//
//                }
            }
            if (forecasts != null && extensions.equals("all") && forecasts.get(0) instanceof JSONObject) {
                JSONObject forecastOb = forecasts.getJSONObject(0);
                cacheWJONSOB.put("forecast", forecastOb);
                kafkaData.put("forecast", forecastOb);
            }
            redisPPMFAndWeatherService.set(adcode, cacheWJONSOB.toJSONString());
        } else {
            log.error("imei: {} adcode:{} 天气预报请求失败 无返回结果:{}", imei, adcode, responseStr);
            //请求失败
            kafkaData.put("errorCode", 10002);
            return 1;
        }
        return 0;
    }

    /**
     * @param [imei, adcode, extensions, kafkaData]
     * @return java.lang.Integer
     * @description 查询百度地图天气预报并发送kafka消息
     * @author cwj
     * @date @time 2024/10/18 11:35
     **/
    private Integer queryBdMapWeatherAndSendMsg(String imei, String adcode, String extensions, JSONObject kafkaData) {
        //随机获取个百度的Web 服务 的Key (能调用天气预报请求接口)
        String ak = listDataProperties.getAmapBdWebLBSKey().get(RandomUtil.getRandomInt(0, listDataProperties.getAmapBdWebLBSKey().size()));
        // base 实况天气预报
        String url = "https://api.map.baidu.com/weather/v1/?";
        Map<String, Object> param = new HashMap<>();
        param.put("district_id", adcode);
        param.put("data_type", extensions);
        param.put("ak", ak);
        param.put("coordtype", "gcj02");
        JSONObject responseJson = HttpReq.sendBdGet(url, param);
        if (StringUtil.isNotBlank(responseJson)) {
            Integer status = responseJson.getInteger("status");
            if (status != 0) {
                int errorCode = 0;
                if (status == 1) {
                    errorCode = 10001;
                }
                kafkaData.put("errorCode", errorCode);
                return 1;
            }
            JSONObject resultJson = responseJson.getJSONObject("result");
            JSONObject nowJson = resultJson.getJSONObject("now");
            JSONObject location = resultJson.getJSONObject("location");
            JSONArray forecasts = resultJson.getJSONArray("forecasts");
            //JSONObject cacheWJONSOB = new JSONObject();
            String upTime = DateUtil.timestampToStr(System.currentTimeMillis(), DateUtil.FORMAT_1);
            //实时天气+最低温或最高温度
            JSONObject weatherJson = new JSONObject();
            if (ObjectUtils.isNotEmpty(nowJson)) {
                //把天气预报和adcode 写到redis中
                long reportTime = System.currentTimeMillis(); //没有拿个当前时间默认的
                upTime = nowJson.getString("uptime");
                if (StringUtil.isNotBlank(upTime)) {
                    //取天气预报的生成时间 此处已被转为毫秒
                    reportTime = DateUtil.strToTimestamp(upTime, DateUtil.FORMAT_2);
                }
                if (Math.abs(System.currentTimeMillis() - reportTime) > 60 * 60 * 24 * 3 * 1000) {
                    // 获取到的天气时间比 当前时间 晚了 3天, 表明可能是adcode 区域编码不正常
                    log.error("获取到的天气时间比 当前时间 晚了 3天, 表明可能是adcode 区域编码不正常, imei:{} url:{}", imei, url);
//                  return 1000;
                }
                upTime = DateUtil.timestampToStr(reportTime, DateUtil.FORMAT_1);
                String weatherStr = String.format("%s,%s,%s度,%s风,%s级,%s更新",
                        //location.getString("city")+location.getString("name"),
                        location.getString("city"),
                        nowJson.get("text"),
                        nowJson.get("temp"),
                        nowJson.get("wind_dir"),
                        nowJson.get("wind_class"),
                        upTime
                );
                //将百度查询结果转成高德的查询结果，以保证前端接口呈现数据的对象无变化
                bdResultTransferGd(weatherJson, location, nowJson, upTime, weatherStr);
                //cacheWJONSOB.put("time", reportTime);
                //cacheWJONSOB.put("weatherCacheMapStr", weatherJson.toJSONString());
                //kafkaData.putAll(weatherJson);
            } else {
                log.error("获取到的天气数据当前实时数据,表明可能是adcode 区域编码不正常，imei:{} url:{}", imei, url);
                //没有拿到天气的数据
                kafkaData.put("errorCode", 10003);
                return 1;
            }
            if (forecasts != null && "all".equals(extensions) && forecasts.get(0) instanceof JSONObject) {
                //取出当天的数据
                JSONObject currnetDayData = getCurrentWetherDate(forecasts);
                if (ObjectUtils.isNotEmpty(currnetDayData)) {
                    if (ObjectUtils.isNotEmpty(weatherJson)) {
                        weatherJson.put("high_temp", currnetDayData.getString("high"));
                        weatherJson.put("low_temp", currnetDayData.getString("low"));
                    }
                } else {
                    log.error("获取到的天气数据没有当天的数据,表明可能是adcode 区域编码不正常，imei:{} url:{}", imei, url);
                    //没有拿到天气的数据
                    kafkaData.put("errorCode", 10003);
                    return 1;
                }
            }
            //将组装的数据存入kafkaJsong中，并推送数据到TCP，组装下发指令数据给设备
            kafkaData.putAll(weatherJson);
            /*if(forecasts != null && "all".equals(extensions) && forecasts.get(0) instanceof JSONObject){
                JSONObject gdForecast = new JSONObject();
                //将百度Forecast转成高德的Forecast，以保证前端接口呈现数据的对象无变化
                bdForecastToGdForecast(gdForecast, location, upTime, forecasts);
                //cacheWJONSOB.put("forecast", gdForecast);
                kafkaData.put("forecast", gdForecast);
            }*/
            //碰碰交好友
            //redisPPMFAndWeatherService.set(adcode, cacheWJONSOB.toJSONString());
        } else {
            log.error("imei: {} adcode:{} 天气预报请求失败 无返回结果:{}", imei, adcode, responseJson);
            //请求失败
            kafkaData.put("errorCode", 10002);
            return 1;
        }
        return 0;
    }

    /**
     * @param [forecasts]
     * @return com.alibaba.fastjson.JSONObject
     * @description 获取当前的天气数据
     * @author cwj
     * @date @time 2025/6/10 17:09
     **/
    private JSONObject getCurrentWetherDate(JSONArray forecasts) {
        JSONObject currnetDayData = null;
        String dayDate = DateUtil.timestampToStr(System.currentTimeMillis(), DateUtil.FORMAT_4);
        for (int i = 0; i < forecasts.size(); i++) {
            JSONObject forecast = forecasts.getJSONObject(i);
            if (dayDate.equals(forecast.getString("date"))) {
                currnetDayData = forecast;
                break;
            }
        }
        return currnetDayData;
    }

    /**
     * @param [gdForecast, location, upTime, forecasts]
     * @return void
     * @description 将百度Forecast转成高德的Forecast
     * @author cwj
     * @date @time 2024/10/18 15:27
     **/
    private void bdForecastToGdForecast(JSONObject gdForecast, JSONObject location, String upTime, JSONArray forecasts) {
        //gdForecast.put("city", location.getString("city") + location.getString("name"));
        gdForecast.put("city", location.getString("city"));
        gdForecast.put("province", location.getString("province"));
        gdForecast.put("reporttime", upTime);
        gdForecast.put("adcode", location.getString("id"));
        JSONArray casts = new JSONArray();
        for (int i = 0; i < forecasts.size(); i++) {
            JSONObject forecast = forecasts.getJSONObject(i);
            JSONObject cast = new JSONObject();
            cast.put("date", forecast.getString("date"));
            cast.put("week", weekToNum(forecast.getString("week")));
            cast.put("dayweather", forecast.getString("text_day"));
            cast.put("nightweather", forecast.getString("text_night"));
            cast.put("daytemp", forecast.getIntValue("high"));
            cast.put("nighttemp", forecast.getIntValue("low"));
            cast.put("daywind", forecast.getString("wd_day"));
            cast.put("nightwind", forecast.getString("wd_night"));
            cast.put("daypower", forecast.getString("wc_day"));
            cast.put("nightpower", forecast.getString("wc_night"));
            cast.put("daytemp_float", forecast.getFloatValue("high"));
            cast.put("nighttemp_float", forecast.getFloatValue("low"));
            casts.add(cast);
        }
        if (ObjectUtils.isNotEmpty(casts)) {
            gdForecast.put("casts", casts);
        }
    }

    /**
     * @param [weatherJson, location, nowJson, upTime, weatherStr]
     * @return void
     * @description 将百度会返回值转成高德的返回值
     * @author cwj
     * @date @time 2024/10/18 14:34
     **/
    private static void bdResultTransferGd(JSONObject weatherJson, JSONObject location, JSONObject nowJson, String upTime, String weatherStr) {
        weatherJson.put("country", location.getString("country"));
        weatherJson.put("province", location.getString("province"));
        //weatherJson.put("city", location.getString("city") + location.getString("name"));
        weatherJson.put("city", location.getString("city"));
        weatherJson.put("adcode", location.getString("id"));
        weatherJson.put("weather", nowJson.getString("text"));
        weatherJson.put("temperature", nowJson.getString("temp"));
        weatherJson.put("winddirection", nowJson.getString("wind_dir"));
        weatherJson.put("windpower", "wind_class");
        weatherJson.put("humidity", nowJson.getString("rh"));
        weatherJson.put("reporttime", upTime);
        weatherJson.put("temperature_float", nowJson.getFloatValue("temp"));
        weatherJson.put("humidity_float", nowJson.getFloatValue("rh"));
        weatherJson.put("weather_str", weatherStr);
    }


    /**
     * @param [week]
     * @return java.lang.Integer
     * @description 将星期转为数字
     * @author cwj
     * @date @time 2024/10/18 15:18
     **/
    private Integer weekToNum(String week) {
        int num = 1;
        switch (week) {
            case "星期一": {
                break;
            }
            case "星期二": {
                num = 2;
                break;
            }
            case "星期三": {
                num = 3;
                break;
            }
            case "星期四": {
                num = 4;
                break;
            }
            case "星期五": {
                num = 5;
                break;
            }
            case "星期六": {
                num = 6;
                break;
            }
            case "星期日": {
                num = 7;
                break;
            }
        }
        return num;
    }

    /**
     * 海外 和风天气预报
     *
     * @param imei
     * @param language
     * @param lngLat
     * @param locationid
     * @param extensions
     * @param kafkaData
     * @time: 2/14/22 4:42 PM
     * @author: Weichao
     * @version 1.0
     */
    private void queryWeatherAndSendMsg_Overseas(String imei, String language, String lngLat, String locationid, String extensions, JSONObject kafkaData) {
        //处理一下语言
        if (language.contains("-")) {
            language = language.split("-")[0];
        } else if (language.contains("_")) {
            language = language.split("_")[0];
        }
        String locationName = "";
        if (locationid == null && lngLat != null) {
            //先获取城市区域Id先
            JSONObject locationInfo = HeFengWeatherQ_LocationidWithLnglat(imei, language, lngLat);
            if (locationInfo == null) {
                //请求失败
                kafkaData.put("errorCode", 10002);
                return;
            }
            locationid = locationInfo.getString("id");
            locationName = locationInfo.getString("name");
        }
        //根据extensions 请求天气信息
        JSONObject weatherInfoOB = HeFengQ_WeatherWithLocationid(imei, language, locationid, extensions);
        if (weatherInfoOB == null) {
            //请求失败
            kafkaData.put("errorCode", 10002);
            return;
        }
        if ("all".equals(extensions)) {
            kafkaData.put("dailyData", weatherInfoOB);
        } else {
            kafkaData.put("nowData", weatherInfoOB);
        }
        kafkaData.put("locationid", locationid);
        kafkaData.put("locationName", locationName);
    }


    /**
     * 和风天气, 根据 经度,纬度 查询Locationid, 并返回结果
     *
     * @param imei
     * @param language
     * @param lngLat
     * @time: 2/16/22 4:51 PM
     * @author: Weichao
     * @version 1.0
     */
    private JSONObject HeFengWeatherQ_LocationidWithLnglat(String imei, String language, String lngLat) {
        //HeFengWeatherKey
        //https://geoapi.qweather.com/v2/city/lookup?[请求参数]
        String req = "https://geoapi.qweather.com/v2/city/lookup?";
        req += "key=" + HeFengWeatherKey;
        req += "&location=" + lngLat;
        req += "&number=1";
        req += "&lang=" + language;

        String results = HttpReq.sendGet(req);
        if (results == null) {
            log.error(" imei:{} 和风天气预报的城市locationid 查询失败  数据无返回 req:{}", imei, req);
            return null;
        }
        JSONObject resultsOB = JSONObject.parseObject(results);
        if ("200".equals(resultsOB.getString("code"))) {
            JSONObject locationInfo = resultsOB.getJSONArray("location").getJSONObject(0);
            String locationid = locationInfo.getString("id");
            Map<String, String> tempMap = new HashMap<>();
            tempMap.put("rawData", locationInfo.toJSONString());
            tempMap.put("locationidAndName", locationid + "&" + locationInfo.getString("name"));
            //缓存
            redisPPMFAndWeatherService.hmset(ConstCacheField.HF_WEATHER_LOCATIONID_IMEI_PREFIX + imei, tempMap);
            return locationInfo;
        } else {
            log.error(" imei:{} 和风天气预报的城市locationid 查询失败 req:{} ==== resultsOB:{}", imei, req, resultsOB.toJSONString());
        }
        return null;

    }

    /**
     * 和风天气, 根据extensions 查询并返回天气信息
     *
     * @param imei
     * @param language
     * @param locationid 区域 ID
     * @param extensions
     * @time: 2/16/22 5:11 PM
     * @author: Weichao
     * @version 1.0
     */
    private JSONObject HeFengQ_WeatherWithLocationid(String imei, String language, String locationid, String extensions) {
        String req;
        if ("all".equals(extensions)) {
            //3天的天气预报
            req = "https://api.qweather.com/v7/weather/3d?";
        } else {
            //实况
            req = "https://api.qweather.com/v7/weather/now?";
        }
        req += "key=" + HeFengWeatherKey;
        req += "&location=" + locationid;
        req += "&lang=" + language;
        String results = HttpReq.sendGet(req);
        if (results == null) {
            log.error(" imei:{} 和风天气预报 查询(extensions:{})天气信息失败 req:{} 数据无返回", imei, extensions, req);
            return null;
        }
        JSONObject resultsOB = JSONObject.parseObject(results);
        if ("200".equals(resultsOB.getString("code"))) {
            //缓存天气信息到 redis 缓存 2小时 (分实时和逐天)
            redisPPMFAndWeatherService.setex(ConstCacheField.HF_WEATHER_INFO_LOCATIONID_PREFIX + locationid + ":" + extensions, 60 * 60 * 2, resultsOB.toJSONString());
            return resultsOB;
        } else {
            log.error(" imei:{} 和风天气预报 查询(extensions:{})天气信息失败 req:{}  ==== resultsOB:{}", imei, extensions, req, resultsOB.toJSONString());
        }
        return null;
    }

    /**
     * 写入轨迹点
     *
     * @param device
     * @param newLoc
     * @param today
     * @param message
     * @time: 2021/2/25 18:38
     * @author: Rock
     * @version 1.0
     */
    private Document setNewloctionFunc(Device device, DeviceLocation newLoc, String today, JSONObject message) {
        Document resultDoc = new Document();
        String id = UUIDGenerator.generate();
        newLoc.setId(id);
        newLoc.setImei(device.getId());
        newLoc.setOriginData(TCPUtil.jsonChangeMap(message));
        //定位轨迹的失效时间是 7天左右 可调整
        newLoc.setExpiryAt(DateUtil.getDevLocExpiryAt());
        //保存定位轨迹
        deviceLocationService.save(newLoc);
        Document newDoc = deviceLocationService.findDocById(id);
        newDoc.remove("i");
        //更新设备loc（旧位置（新轨迹））的信息
        deviceService.updateById(device.getId(), new Update().set("loc", newDoc));
        resultDoc.putAll(newDoc);
        resultDoc.put("i", device.getId());
        resultDoc.remove("expiryAt");

        String td = DateUtil.dateToStr(new Date(), DateUtil.FORMAT_4);
        Query query = new Query(Criteria.where("i").is(device.getId()).and("day").is(td));
        DeviceLocationDaily deviceLocationDaily = deviceLocationDailyService.findOne(query);
        if (deviceLocationDaily == null) {
            if (StringUtil.isNotBlank(today)) {
                deviceLocationDaily = new DeviceLocationDaily();
                deviceLocationDaily.setImei(device.getId());
                deviceLocationDaily.setDate(DateUtil.strToDate(td));
                deviceLocationDaily.setDay(today);
                deviceLocationDaily.setSum(1);
                deviceLocationDailyService.save(deviceLocationDaily);
            } else {
                query = new Query(Criteria.where("i").is(device.getId()));
                List<DeviceLocationDaily> list = deviceLocationDailyService.find(query.with(Sort.by(Sort.Order.asc("_id"))).limit(1));
                if (list != null && list.size() > 0) {
                    deviceLocationDaily = list.get(0);
                    log.info("deviceLocationDaily ==== {}", deviceLocationDaily.toString());
                    if (td.equals(DateUtil.dateToStr(deviceLocationDaily.getDate(), DateUtil.FORMAT_4))) {
                        long startTime = DateUtil.strToTimestamp(td, DateUtil.FORMAT_4) / 1000;
                        long endTime = startTime + 86400l;
                        query = new Query(Criteria.where("i").is(device.getId()).and("isAbnormal").exists(true)
                                .andOperator(Criteria.where("t").gte(startTime), Criteria.where("t").lte(endTime)));
                        long sum = deviceLocationService.count(query);
                        deviceLocationDailyService.updateById(new ObjectId(deviceLocationDaily.getId()),
                                new Update().set("sum", sum).set("day", td));
                    } else {
                        deviceLocationDaily = new DeviceLocationDaily();
                        deviceLocationDaily.setImei(device.getId());
                        deviceLocationDaily.setDate(DateUtil.strToDate(td));
                        deviceLocationDaily.setDay(td);
                        deviceLocationDaily.setSum(1);
                        deviceLocationDailyService.save(deviceLocationDaily);
                    }
                } else {
                    long startTime = DateUtil.strToTimestamp(td, DateUtil.FORMAT_4) / 1000;
                    long endTime = startTime + 86400l;
                    query = new Query(Criteria.where("i").is(device.getId())
                            .andOperator(Criteria.where("t").gte(startTime), Criteria.where("t").lte(endTime)));
                    long sum = deviceLocationService.count(query);
                    deviceLocationDaily = new DeviceLocationDaily();
                    deviceLocationDaily.setImei(device.getId());
                    deviceLocationDaily.setDate(DateUtil.strToDate(td));
                    deviceLocationDaily.setDay(td);
                    deviceLocationDaily.setSum((int) sum);
                    deviceLocationDailyService.save(deviceLocationDaily);
                }
            }
        } else {
            deviceLocationDailyService.updateById(new ObjectId(deviceLocationDaily.getId()), new Update().set("sum", deviceLocationDaily.getSum() + 1));
        }
        return resultDoc;
    }


   /* private Integer queryBdMapWeatherAndSendMsg2(String imei, String adcode, String extensions, JSONObject kafkaData) {
        //随机获取个百度的Web 服务 的Key (能调用天气预报请求接口)
        String ak = "lRAkXOQ5SEF0YMFzHZfIa3ZHdqC8y0U2"; //listDataProperties.getAmapBdWebLBSKey().get(RandomUtil.getRandomInt(0, listDataProperties.getAmapBdWebLBSKey().size()));
        // base 实况天气预报
        String url = "https://api.map.baidu.com/weather/v1/?";
        Map<String,Object> param = new HashMap<>();
        param.put("district_id",adcode);
        param.put("data_type",extensions);
        param.put("ak",ak);
        param.put("coordtype","gcj02");
        JSONObject responseJson = HttpReq.sendBdGet(url, param);
        if (StringUtil.isNotBlank(responseJson)) {
            Integer status = responseJson.getInteger("status");
            if (status != 0){
                int errorCode = 0;
                if(status == 1){
                    errorCode = 10001;
                }
                kafkaData.put("errorCode", errorCode);
                return 1;
            }
            JSONObject resultJson = responseJson.getJSONObject("result");
            JSONObject nowJson = resultJson.getJSONObject("now");
            JSONObject location = resultJson.getJSONObject("location");
            JSONArray forecasts = resultJson.getJSONArray("forecasts");
            JSONObject cacheWJONSOB = new JSONObject();
            String upTime = DateUtil.timestampToStr(System.currentTimeMillis(),DateUtil.FORMAT_1);
            if (ObjectUtils.isNotEmpty(nowJson)) {
                //实时天气
                JSONObject weatherJson = new JSONObject();
                //把天气预报和adcode 写到redis中
                long reportTime = System.currentTimeMillis(); //没有拿个当前时间默认的
                upTime = nowJson.getString("uptime");
                if (StringUtil.isNotBlank(upTime)) {
                    //取天气预报的生成时间 此处已被转为毫秒
                    reportTime = DateUtil.strToTimestamp(upTime, DateUtil.FORMAT_2);
                }
                if(Math.abs(System.currentTimeMillis() - reportTime) > 60*60*24*3 * 1000){
                    // 获取到的天气时间比 当前时间 晚了 3天, 表明可能是adcode 区域编码不正常
                    log.error("获取到的天气时间比 当前时间 晚了 3天, 表明可能是adcode 区域编码不正常, imei:{} url:{}", imei,url);
//                    return 1000;
                }
                upTime = DateUtil.timestampToStr(reportTime,DateUtil.FORMAT_1);
                String weatherStr = String.format("%s,%s,%s度,%s风,%s级,%s更新",
                        location.getString("city")+location.getString("name"),
                        nowJson.get("text"),
                        nowJson.get("temp"),
                        nowJson.get("wind_dir"),
                        nowJson.get("wind_class"),
                        upTime
                );
                //将百度查询结果转成高德的查询结果，以保证前端接口呈现数据的对象无变化
                bdResultTransferGd(weatherJson, location, nowJson, upTime, weatherStr);
                cacheWJONSOB.put("time", reportTime);
                cacheWJONSOB.put("weatherCacheMapStr", weatherJson.toJSONString());
                kafkaData.putAll(weatherJson);
            }
            if(forecasts != null && extensions.equals("all") && forecasts.get(0) instanceof JSONObject){
                JSONObject gdForecast = new JSONObject();
                //将百度Forecast转成高德的Forecast，以保证前端接口呈现数据的对象无变化
                bdForecastToGdForecast(gdForecast, location, upTime, forecasts);
                cacheWJONSOB.put("forecast", gdForecast);
                kafkaData.put("forecast", gdForecast);
            }
            //redisPPMFAndWeatherService.set(adcode, cacheWJONSOB.toJSONString());
        }else{
            log.error("imei: {} adcode:{} 天气预报请求失败 无返回结果:{}", imei, adcode, responseJson);
            //请求失败
            kafkaData.put("errorCode", 10002);
            return 1;
        }
        return null;
    }*/

    public static void main(String[] args) {
        //点1 准   维智:龙岗   高德:惠阳  2024-04-25 13:46:08
        String tempStr = "{ \"feedback\" : 1, \"baseStationType\" : 0, \"wifi\" : [ { \"channel\" : 0, \"signal\" : \"-29\", \"ssid\" : \"\", \"mac\" : \"12:f0:d9:3e:16:83\" }, { \"channel\" : 0, \"signal\" : \"-91\", \"ssid\" : \"\", \"mac\" : \"44:56:e2:a4:ea:89\" }, { \"channel\" : 0, \"signal\" : \"-91\", \"ssid\" : \"\", \"mac\" : \"00:e2:2c:71:02:d0\" }, { \"channel\" : 0, \"signal\" : \"-91\", \"ssid\" : \"\", \"mac\" : \"04:95:e6:e0:60:31\" }, { \"channel\" : 0, \"signal\" : \"-91\", \"ssid\" : \"\", \"mac\" : \"b4:1d:2b:6a:1c:3a\" }, { \"channel\" : 0, \"signal\" : \"-93\", \"ssid\" : \"\", \"mac\" : \"34:12:f9:89:eb:a1\" } ], \"ident\" : 120977, \"kafkaModelType\" : 1, \"imei\" : \"869029066032607\", \"locationType\" : \"ordinary\", \"baseStation\" : [ { \"connected\" : 1, \"mnc\" : 0, \"ci\" : 24077067, \"rxlev\" : 83, \"mcc\" : 460, \"type\" : \"lte\", \"lac\" : 9553, \"timestamp\" : 1714023967 }, { \"connected\" : 0, \"mnc\" : 0, \"ci\" : 24077067, \"rxlev\" : 83, \"mcc\" : 460, \"type\" : \"lte\", \"lac\" : 9553, \"timestamp\" : 1714023967 }, { \"connected\" : 0, \"mnc\" : 0, \"ci\" : 24077067, \"rxlev\" : 83, \"mcc\" : 460, \"type\" : \"lte\", \"lac\" : 9553, \"timestamp\" : 1714023967 } ], \"time\" : 1714023968, \"vender\" : \"20000\", \"type\" : \"location\", \"location_time\" : 1714023968 }";
        //点2 不准  维智:惠阳   高德:惠阳  2024-04-25 13:56:08
        tempStr = "{ \"feedback\" : 1, \"baseStationType\" : 0, \"wifi\" : [ { \"channel\" : 0, \"signal\" : \"-28\", \"ssid\" : \"\", \"mac\" : \"12:f0:d9:3e:16:83\" }, { \"channel\" : 0, \"signal\" : \"-78\", \"ssid\" : \"\", \"mac\" : \"44:33:4c:a1:4b:93\" }, { \"channel\" : 0, \"signal\" : \"-81\", \"ssid\" : \"\", \"mac\" : \"88:12:ac:f6:96:1f\" }, { \"channel\" : 0, \"signal\" : \"-91\", \"ssid\" : \"\", \"mac\" : \"94:a1:a2:14:f3:50\" }, { \"channel\" : 0, \"signal\" : \"-91\", \"ssid\" : \"\", \"mac\" : \"de:57:71:f3:22:52\" } ], \"ident\" : 370485, \"kafkaModelType\" : 1, \"imei\" : \"869029066032607\", \"locationType\" : \"ordinary\", \"baseStation\" : [ { \"connected\" : 1, \"mnc\" : 0, \"ci\" : 24077067, \"rxlev\" : 83, \"mcc\" : 460, \"type\" : \"lte\", \"lac\" : 9553, \"timestamp\" : 1714024568 }, { \"connected\" : 0, \"mnc\" : 0, \"ci\" : 24077067, \"rxlev\" : 83, \"mcc\" : 460, \"type\" : \"lte\", \"lac\" : 9553, \"timestamp\" : 1714024568 }, { \"connected\" : 0, \"mnc\" : 0, \"ci\" : 24077067, \"rxlev\" : 83, \"mcc\" : 460, \"type\" : \"lte\", \"lac\" : 9553, \"timestamp\" : 1714024568 } ], \"time\" : 1714024568, \"vender\" : \"20000\", \"type\" : \"location\", \"location_time\" : 1714024568 }";
        //点3 准    维智:龙岗  高德:惠阳  2024-04-25 14:06:06
//        tempStr = "{ \"feedback\" : 1, \"baseStationType\" : 0, \"wifi\" : [ { \"channel\" : 0, \"signal\" : \"-68\", \"ssid\" : \"\", \"mac\" : \"26:9b:27:f2:80:01\" }, { \"channel\" : 0, \"signal\" : \"-68\", \"ssid\" : \"\", \"mac\" : \"f2:74:8d:df:41:ec\" }, { \"channel\" : 0, \"signal\" : \"-74\", \"ssid\" : \"\", \"mac\" : \"ee:60:73:2f:14:15\" }, { \"channel\" : 0, \"signal\" : \"-79\", \"ssid\" : \"\", \"mac\" : \"60:2e:20:e6:d3:14\" }, { \"channel\" : 0, \"signal\" : \"-81\", \"ssid\" : \"\", \"mac\" : \"f4:bf:80:b2:27:05\" }, { \"channel\" : 0, \"signal\" : \"-82\", \"ssid\" : \"\", \"mac\" : \"f4:bf:80:b2:27:04\" }, { \"channel\" : 0, \"signal\" : \"-85\", \"ssid\" : \"\", \"mac\" : \"08:9b:4b:9d:5c:c5\" } ], \"ident\" : 558712, \"kafkaModelType\" : 1, \"imei\" : \"869029066032607\", \"locationType\" : \"ordinary\", \"baseStation\" : [ { \"connected\" : 1, \"mnc\" : 0, \"ci\" : 24077067, \"rxlev\" : 83, \"mcc\" : 460, \"type\" : \"lte\", \"lac\" : 9553, \"timestamp\" : 1714025166 }, { \"connected\" : 0, \"mnc\" : 0, \"ci\" : 24077067, \"rxlev\" : 83, \"mcc\" : 460, \"type\" : \"lte\", \"lac\" : 9553, \"timestamp\" : 1714025166 }, { \"connected\" : 0, \"mnc\" : 0, \"ci\" : 24077067, \"rxlev\" : 83, \"mcc\" : 460, \"type\" : \"lte\", \"lac\" : 9553, \"timestamp\" : 1714025166 } ], \"time\" : 1714025166, \"vender\" : \"20000\", \"type\" : \"location\", \"location_time\" : 1714025166 }";


        //龙岗2  维智:龙岗   高德:惠阳  2024-04-25 14:06:06
//        tempStr = "{ \"feedback\" : 1, \"baseStationType\" : 0, \"wifi\" : [ { \"channel\" : 0, \"signal\" : \"-68\", \"ssid\" : \"\", \"mac\" : \"26:9b:27:f2:80:01\" }, { \"channel\" : 0, \"signal\" : \"-68\", \"ssid\" : \"\", \"mac\" : \"f2:74:8d:df:41:ec\" }, { \"channel\" : 0, \"signal\" : \"-74\", \"ssid\" : \"\", \"mac\" : \"ee:60:73:2f:14:15\" }, { \"channel\" : 0, \"signal\" : \"-79\", \"ssid\" : \"\", \"mac\" : \"60:2e:20:e6:d3:14\" }, { \"channel\" : 0, \"signal\" : \"-81\", \"ssid\" : \"\", \"mac\" : \"f4:bf:80:b2:27:05\" }, { \"channel\" : 0, \"signal\" : \"-82\", \"ssid\" : \"\", \"mac\" : \"f4:bf:80:b2:27:04\" }, { \"channel\" : 0, \"signal\" : \"-85\", \"ssid\" : \"\", \"mac\" : \"08:9b:4b:9d:5c:c5\" } ], \"ident\" : 558712, \"kafkaModelType\" : 1, \"imei\" : \"869029066032607\", \"locationType\" : \"ordinary\", \"baseStation\" : [ { \"connected\" : 1, \"mnc\" : 0, \"ci\" : 24077067, \"rxlev\" : 83, \"mcc\" : 460, \"type\" : \"lte\", \"lac\" : 9553, \"timestamp\" : 1714025166 } ], \"time\" : 1714025166, \"vender\" : \"20000\", \"type\" : \"location\", \"location_time\" : 1714025166 }";

        TCPService tcpService = new TCPService();
        Device device = new Device();
        device.setId("869029066032607");
        //tcpService.queryBdMapWeatherAndSendMsg2("869029066032607","440106","all",resJson);
        JSONObject resJson = new JSONObject();
        //tcpService.queryBdMapWeatherAndSendMsg2("869029066032607","440106","all",resJson);
//        tcpService.executionLocateFuntion(device.getId(), JSONObject.parseObject(tempStr), device, resJson);
        //tcpService.toWZ_LBSparsing(JSONObject.parseObject(tempStr), device, resJson);

        tcpService.toGD_LBSparsing(JSONObject.parseObject(tempStr), device, resJson);

        tcpService.toBD_LBSparsing(JSONObject.parseObject(tempStr), device, resJson);
//
//        TCPService tcpService = new TCPService();
//        //模拟 海外上传的基站信息及 GPS点  lngLat : -81.37,28.73
//        JSONObject kafkaJSON = new JSONObject();
//        tcpService.queryWeatherAndSendMsg_Overseas("测试IMEI", "zh", "-81.37,28.73", null, "all", kafkaJSON);
//        System.out.println(String.format("kafkaJSON:%s", kafkaJSON.toJSONString()));


    }
}
